Sunday, January 22, 2012
■ [Metro]Metro style appsと解像度と画面サイズと画面解像度

このエントリの内容はWindows Developer Previewの仕様を元にしています。今後Betaやリリース時には変更される可能性があることに注意してください。
Metro style appsの解像度に合わせたスケーリング - ういはるかぜの化学 - subtech でスケーリングについて書きましたが、その拡大率が決定される条件がちょっと謎な感じに見えます。たとえば同じ画面解像度1920x1080でも10.6インチと23インチとで拡大率が違うわけです。
しかもVAIO Z(13インチ, 1920x1080)にWindows Developer Previewをインストールするとなぜかシステム解像度(以前からあるあのDPI設定)がデフォルトで120dpiになっています。その割にはMetro style appsはスケーリングしません。どうも画面サイズ(物理的な画面サイズ)をWindows側で把握してコントロールしているような感じです。
画面サイズの取得なんてとれるのかなと思って調べてみると GetDeviceCaps 関数というWin32 APIにVERTSIZE,HORZSIZEを渡すと取れそうなのですがいろいろなところでまともな値は返さない(物理サイズの参考にはならない)という声も見かけます。
まあそうはいっても他に見当たらないのでコードを書いてとりあえず試してみました。
https://gist.github.com/1656292
書いたコードをVAIO L (24インチ, 1920x1080)で実行したところ530x300mmという値が返ってきました。24インチの16:9で53.13cm x 29.89cmぐらいらしいのでだいたいあってそうです。ならばとVAIO Z (13インチ, 1920x1080)で試してみると今度は290x160mmという値が返ってきました。やっぱりちゃんと動いているっぽいです。
Windows 7で動かすと677x381mmという明らかに変な値が出てくることからWindows 8からまともに機能するようになったようです。
というわけでこれをシミュレータで動かしたりして、さらにそのサイズからDPIを求めたりしてまとめたのが以下の表です。
| 環境 | 画面サイズ(インチ) | Windowsが把握している画面サイズ | 画面解像度 | 画面サイズと画面解像度で求めたDPI | 拡大率 |
|---|---|---|---|---|---|
| VAIO L | 24 | 530x300mm | 1920x1080 | 約92dpi x 91dpi | 100% |
| VAIO Z | 13 | 290x160mm | 1920x1080 | 約168dpi x 171dpi | 100% |
| シミュレーター | 10.6 | 132x235mm | 1366x768 | 約148dpi | 100% |
| 1920x1080 | 約207dpi | 140% | |||
| 2560x1440 | 約276dpi | 180% | |||
| 12 | 1280x800 | 162x258mm | 約126dpi | 100% | |
| 23 | 286x509mm | 1920x1080 | 約96dpi | 100% | |
| 27 | 336x598mm | 2560x1440 | 約109dpi | 100% |
これでどういう意味があるかと言うと、どうもWindows 8は10.6インチ(1366x768)をスタンダード スレートと定義しているらしく(ガイドラインの一番上の図がそんな感じ)そこを基準にいろいろ決まっているようです。
たとえば10.6インチの1920x1080では140%に拡大されるのに13インチで1920x1080のVAIO Zでは何も起こらないのは、
- 10.6インチの1920x1080は約207dpiで140%の拡大率
- 拡大率140%は約207dpiが基準になり、それを超えると適用される
- VAIO Zでは約170dpi前後ぐらいしかないので100%の域になる
となっていると推測できます。画面サイズで決定されるので拡大率にシステムのDPI設定は考慮されないことになります。
これで画面解像度だけで拡大率が決まるわけではない理由がなんとなくわかりました。
CSSのメディアクエリーで使われていた謎のdpi値
MSDNのガイドラインでは以下のようなメディアクエリーの例が書かれていました(微妙に間違ってるので直してますけど…)。
/* CSS - 拡大率に応じてリモートの画像を読み込む */ @media all and (max-resolution: 134dpi){ /* 100%の拡大率のとき100%用の画像 */ .imageBackground { background-image: url('http://www.fabrikam.com/foo.png?s=100'); } } @media all and (min-resolution: 135dpi) { /* 140%の拡大率のとき140%用の画像 */ .imageBackground { background-image: url('http://www.fabrikam.com/foo.png?s=140'); } } @media all and (min-resolution: 173dpi) { /* 180%の拡大率のとき180%用の画像 */ /* MSDNのサンプルでは174dpiになっているが173dpiでないと動かない */ .imageBackground { background-image: url('http://www.fabrikam.com/foo.png?s=180'); } }
134dpi、135dpi、173dpiとありますがこれは100%を96dpiとしたときの値です。つまり
- 96dpi * 100% = 96dpi
- 96dpi * 140% = 134.40dpi (135dpi)
- 96dpi * 180% = 172.8dpi (173dpi)
となります。Windowsは96dpiが標準解像度となっているのでそれが基準になっているのですね。
- 一つ目はmax-resolution:134dpiなので134dpiまでなので135dpi(140%)にならない範囲ぎりぎり→100%
- 二つ目はmin-resolution:135dpiなので140%の時の135dpi以上
- 三つ目はmin-resolution:173dpiなので180%の時の173dpi以上
に適用されることになるわけです。
ちなみにこのDPI値はHTML+JavaScriptなMetro style appsではwindow.screen.deviceXDPIとして取得できます。WinRTを経由してもWindows.Graphics.Display.DisplayProperties.LogicalDpiプロパティを使うことで取得できます。
が、なぜか180%の時の値が違います。deviceXDPIは173を返しますがLogicalDpiは174を返します。MSDNのメディアクエリーの例に174dpiと書いてあったのはWinRTの値が元なのではないかと思います。実際ダウンロードできるサンプルコードの方では173dpiになっています。まあ計算しても違うわけですしWinRTのバグのような気がします。
■ [Metro]Metro style appsの解像度に合わせたスケーリング

このエントリの内容はWindows Developer Previewの仕様を元にしています。今後Betaやリリース時には変更される可能性があることに注意してください。
昨今のスマートフォンやタブレットは画面が高精細化してきつつあり、アプリケーションも解像度に応じた対応を迫られ、iPhone 4以降ではRetina Display対応といった風にアプリケーションが対応しているのを見かけるようになりました。
Windows 8(コードネーム)はこれからリリースされるOSですのでそのあたりも考慮されています。そこでMetro style appsにおいてはアプリケーションはどのようにして対応するのかガイドライン(Guidelines for scaling to pixel density)があるのでちょっと解説してみます。
スケーリングのされ方
Metro style appsでは解像度と画面の物理サイズによって100%,140%,180%と3段階の拡大率を持ち自動的に適用されます。拡大というのはブラウザの拡大みたいなものです。
たとえば10.6インチの1366x768では拡大なしですが、同じ物理画面サイズで1920x1080の場合には140%に拡大されるといった感じです。また、同じ1920x1080でも10.6インチ(スレートサイズ)と23インチ(デスクトップ)では解像度(DPI)が違うので拡大率も違ったりします。この辺は別に書きます(→Metro style appsと解像度と画面サイズと画面解像度 - ういはるかぜの化学 - subtech)。
| 拡大率 | 画面サイズ |
|---|---|
| 100% | 10.6インチ/1366x768, 23インチ/1920x1080 |
| 140% | 10.6インチ/1920x1080 |
| 180% | 10.6インチ/2560x1440 |
アプリケーションですべきこと
自動的に拡大されると書きましたが、ただ拡大するだけではキレイになるとは限らないのでアプリケーション側での対応も必要となります。ガイドライン(Guidelines for scaling to pixel density)によれば以下のことをするようにと書かれています。
スケーラブルなベクターグラフィックスを使う
解像度非依存なベクター画像を使いましょうという話です。ベクター画像であれば伸び縮みさせてもジャギジャギしたりもやもやしたりしません(Windowsがうまくレンダリングします)。
HTMLでアプリケーションを作る場合にはSVGを、C#などで作る場合にはXAMLを使うことで実現します。
アプリケーションパッケージからビットマップ画像を読み込む
ビットマップ画像(JPEG,PNGなど)に特定のルールに従って名前をつけておくと自動的に拡大率の状態に応じて読み込むファイルを変える機能があります。
この機能によって拡大率の違いによってサイズの違う画像を読み込むことができます。
たとえばHTMLでは以下のようにimg要素でファイル /img/Image.png を参照します。
<img src="/img/Image.png" alt="Image" />
この時、実際に読み込まれるファイルは /img/Image.png だけでなく以下の拡大率によってバリエーションも参照されます。
- /img/Image.scale-100.png (100%時)
- /img/Image.scale-140.png (140%時)
- /img/Image.scale-180.png (180%時)
- /img/scale-100/Image.png (100%時)
- /img/scale-140/Image.png (140%時)
- /img/scale-180/Image.png (180%時)
この例のようにファイル名に .scale-{100,140,180} を含めるか、フォルダとして scale-{100,140,180} として分けるか好きな置き方を選ぶことができます。これはHTMLからの参照だけでなくCSSからの参照(たとえばbackground-imageプロパティ)も対象となります。
外部の画像を参照する場合はメディアクエリーを使う
外部つまりリモートにある画像を参照する場合には拡大率によって自動でバリエーションを検索する機能は動作しません。
そこでHTML ベースなMetro style appsでCSSから参照する場合にはメディアクエリーで解像度に応じて読み込む画像を切り替えます。
たとえばMSDNにある例だと以下のようなCSSになっています。
/* CSS - 拡大率に応じてリモートの画像を読み込む */ @media all and (max-resolution: 134dpi){ /* 100%の拡大率のとき100%用の画像 */ .imageBackground { background-image: url('http://www.fabrikam.com/foo.png?s=100'); } } @media all and (min-resolution: 135dpi) { /* 140%の拡大率のとき140%用の画像 */ .imageBackground { background-image: url('http://www.fabrikam.com/foo.png?s=140'); } } @media all and (min-resolution: 173dpi) { /* 180%の拡大率のとき180%用の画像 */ /* MSDNのサンプルでは174dpiになっているが173dpiでないと動かない */ .imageBackground { background-image: url('http://www.fabrikam.com/foo.png?s=180'); } }
min-resolution/max-resolutionの値が謎のdpiになっている理由は別に書くことにしますが、メディアクエリーのパターンとしては謎のdpiの指定も含めてこのようになります。
ユーザーの画像をファイルシステムから取り出すときにはサムネイルAPIを使う
ユーザーが持っている画像を動的に読み出して使う場合にはWinRTにあるサムネイルAPIを使います。サムネイルAPIはWindowsが現在の拡大率に最適な画像を返します。
サムネイルAPIというのはWindows.Storage名前空間にあるStorageFileクラスが持つ、GetThumbnailAsyncメソッドのことです。StorageFileクラスはフォルダなどから参照したときにとれるものです(よくあるFileクラス的なもの)。
参照:StorageFile.GetThumbnailAsync(ThumbnailMode) | getThumbnailAsync(ThumbnailMode) method
画像を実行時に読み込む場合には拡大率をみて読み込む
JavaScriptなどから動的に画像を読み込む場合にはコードで現在の拡大率によって画像の参照をコントロールする必要があります。
現在の拡大率はWindows.Graphics.Display.DisplayPropertiesクラスのResolutionScaleプロパティを参照することで得ることができます。
switch (Windows.Graphics.Display.DisplayProperties.resolutionScale) { case Windows.Graphics.Display.ResolutionScale.scale100Percent: console.log('100%'); break; case Windows.Graphics.Display.ResolutionScale.scale140Percent: console.log('140%'); break; case Windows.Graphics.Display.ResolutionScale.scale180Percent: console.log('180%'); break; }
画像には幅と高さを指定する
拡大率で読み込む画像が変わったりした場合にレイアウトが崩れるのを防ぐため、画像には幅と高さを指定するようにします。
グリッドをつかう
要するにメジャーなグリッドを20px、マイナーなグリッドを5pxずつとして画面を設計することで拡大した場合にも中途半端な値(たとえば23px*1.4倍=32.2pxに対して25px*1.4倍=35px)にならずピクセルの丸めとズレのようなものが発生しなくなってきれいです。
アプリケーションでしてはいけないこと
スケーリングを考慮する上で逆に避けるべきこともガイドラインには書かれています。
小さい画像を拡大時に使うこと
小さい画像というか100%用の標準的なサイズだけしか用意されていない場合、高解像度環境でスケーリングが必要な場合自動的に拡大されてぼやけた見た目になることになります。ということで画像は各拡大率にあわせたものを用意しておきましょう。
iPhoneで言うとRetina非対応なアプリをiPhone 4で実行したようなことになるのでという話です。
大きい画像を小さくして使うこと
小さい画像を高解像度で使うのと逆に、高解像度用の画像を低い拡大率で使うと縮小するとジャギジャギしてしまうので避けましょう。たとえば180%のパターンだけ用意してそれを140%と100%でも使い回す、といったことです。
ただし例外的に写真の画像は縮小しても見た目上問題ないのであればよいでしょう。
5pxの倍数以外でサイズを指定すること
上にも書きましたが5pxの倍数以外でサイズを指定すると拡大したときにズレが生じてしまいますので避けましょう。
スケーリングの状態を試す
スケーリングの仕組みが用意されていてもどうやってテストすれば?となるわけですが、シミュレータには解像度と画面サイズを想定した設定が用意されていてこれでテストすることができます。
参考までに設定時の状態を以下にまとめておきます。
| 拡大率 | 設定 |
|---|---|
| 100% | 10.6インチ/1366x768, 12インチ/1280x800, 23インチ/1920x1080, 27インチ/2560x1440 |
| 140% | 10.6インチ/1920x1080 |
| 180% | 10.6インチ/2560x1440 |

