DPI と DIP

この記事では、物理ピクセルとデバイスに依存しないピクセル (DIP) の違いと、Win2D での DPI (1 インチあたりのドット数) の処理方法について説明します。

Win2D は、低 DPI デバイスと高 DPI デバイスの両方で実行するときに適切な動作を行う適切な既定の動作を提供しているため、多くのアプリがこの区別を無視できるように設計されています。 アプリに特殊なニーズがある場合、または "賢明な既定値" の意味について異なる意見がある場合は、詳細を確認してください。

DPI とは

DPI は "dots per inch" を表します。 これは、コンピューター モニターや電話画面などの出力ディスプレイのピクセル密度のおおよその測定値です。 DPI が高いほど、ドットが小さくなるほどディスプレイが構成されます。

DPI は、すべてのディスプレイ ハードウェアが正確な情報を報告するために依存できるわけではないため、概数メジャーにすぎません。 一部のコンピューター モニターは、DPI をオペレーティング システムにまったく報告しないか、ユーザーが実際のハードウェアとは異なる DPI を使用してレンダリングするようにシステムを構成している可能性があります (UI テキスト要素のサイズを変更する場合など)。 アプリケーションでは DPI を使用して、描画する大きさを選択できますが、ディスプレイ サイズの正確な物理的な測定として使用しないでください。

DPI 値 96 は、ニュートラルな既定値と見なされます。

ピクセルとは

ピクセルは 1 色のドットです。 コンピューター グラフィックスの画像は、2 次元グリッドに配置された多数のピクセルで構成されます。 ピクセルは、すべての画像が作成されるアトムと考えることができます。

ピクセルの物理サイズは、ディスプレイによって大きく異なる場合があります。 コンピューターが大きいが低解像度のモニターまたは外部ディスプレイに接続されている場合、ピクセルは非常に大きくなる可能性がありますが、1080p ディスプレイを搭載した電話では、ピクセルは小さいです。

Win2D では、整数データ型 (または整数を含む BitmapSize などの構造体) を使用して位置またはサイズを指定する API が表示されるたびに、API がピクセル単位で動作していることを意味します。

ほとんどの Win2D API は、ピクセルではなく DIP で動作します。

DIP とは

DIP は、"デバイスに依存しないピクセル" を表します。 これは、物理ピクセルと同じ、大きい、または小さい可能性がある仮想化ユニットです。

ピクセルと DIP の比率は DPI によって決まります。

pixels = dips * dpi / 96

DPI が 96 の場合、ピクセルと DIP は同じです。 高 DPI を使用する場合、1 つの DIP が複数のピクセル (DPI が 96 の正確な倍数ではない一般的な場合はピクセルの一部) に対応する場合があります。

Win2D を含むほとんどのWindows ランタイム API では、ピクセルではなく DIP が使用されます。 これは、アプリが実行されているディスプレイに関係なく、グラフィックスをほぼ同じ物理サイズに保つという利点があります。 たとえば、アプリでボタンの幅が 100 DIP であると指定されている場合、電話や 4k モニターなどの高 DPI デバイスで実行すると、このボタンは自動的に 100 ピクセルを超える幅にスケーリングされるため、ユーザーがクリックできる適切なサイズのままです。 一方、ボタンのサイズがピクセル単位で指定されている場合、この種の高 DPI ディスプレイではとんでもなく小さく表示されるため、アプリは画面の種類ごとにレイアウトを異なる方法で調整するために、より多くの作業を行う必要があります。

Win2D では、浮動小数点データ型 (または Vector2 や浮動小数点値を含むサイズなどの構造体) を使用して位置またはサイズを指定する API が表示されるたびに、API が DIP で動作していることを意味します。

DIP とピクセルの間で変換するには、 ConvertDipsToPixels(Single, CanvasDpiRounding) メソッドと ConvertPixelsToDips(Int32)メソッドを使用します。

DPI を持つ Win2D リソース

ビットマップ イメージを含むすべての Win2D リソースにも、DPI プロパティが関連付けられています。

その他のリソースの種類はすべて DPI に依存しません。 たとえば、1 つの CanvasDevice インスタンスを使用して、さまざまな DPI のコントロールやレンダーターゲットを描画できるため、デバイスには独自の DPI はありません。

同様に、 CanvasCommandList には、ビットマップ イメージではなくベクター描画命令が含まれているため、DPI はありません。 DPI は、コマンド リストがレンダーターゲットまたはコントロール (DPI を持つ) に描画されるときに、ラスター化プロセス中にのみ機能します。

DPI を制御する

Win2D コントロール (CanvasControlCanvasVirtualControlCanvasAnimatedControl) では、アプリが実行されているディスプレイと同じ DPI が自動的に使用されます。 これは、XAML、CoreWindow、およびその他のWindows ランタイム API で使用される座標系と一致します。

DPI が変更された場合 (アプリが別のディスプレイに移動された場合など)、コントロールは CreateResources イベントを発生させ、 CanvasCreateResourcesReasonDpiChangedを渡します。 アプリは、コントロールの DPI に依存するすべてのリソース (レンダー ターゲットなど) を再作成することで、このイベントに応答する必要があります。

Rendertarget DPI

描画可能なもの ( CanvasRenderTarget だけでなく、レンダーターゲットに似た型 CanvasSwapChainCanvasImageSourceも含む) には独自の DPI がありますが、コントロールとは異なり、これらの型はディスプレイに直接接続されないため、Win2D は DPI を自動的に決定できません。 後で画面にコピーされるレンダーターゲットに描画する場合は、そのレンダーターゲットで画面と同じ DPI を使用したい場合がありますが、他の目的で描画する場合 (たとえば、Web サイトにアップロードするための画像を生成する) には、既定の 96 DPI が適しています。

これらの両方の使用パターンを簡単にするために、Win2D には 2 種類のコンストラクター オーバーロードが用意されています。

CanvasRenderTarget(ICanvasResourceCreator, width, height, dpi)
CanvasRenderTarget(ICanvasResourceCreatorWithDpi, width, height)

ICanvasResourceCreator インターフェイスは、CanvasDeviceと Win2D コントロールによって実装されます。 デバイスにはそれ自体の特定の DPI がないため、レンダーターゲットを作成するときに DPI を明示的に指定する必要があります。

たとえば、DIP とピクセルが常に同じになる既定の DPI レンダーターゲットを作成するには、次のようにします。

const float defaultDpi = 96;
var rtWithFixedDpi = new CanvasRenderTarget(canvasDevice, width, height, defaultDpi);

ICanvasResourceCreatorWithDpi は、DPI プロパティを追加して ICanvasResourceCreator を拡張します。 このインターフェイスは Win2D コントロールによって実装され、作成元のコントロールと同じ DPI を自動的に継承するレンダーターゲットを簡単に作成できます。

var rtWithSameDpiAsDisplay = new CanvasRenderTarget(canvasControl, width, height);

ビットマップ DPI

CanvasBitmapは、レンダーターゲットとは異なり、コントロールから DPI を自動的に継承しません。 ビットマップを作成および読み込む方法には、DPI を明示的に指定するためのオーバーロードが含まれますが、これを省略すると、ビットマップ DPI は現在の表示構成に関係なく既定で 96 になります。

ビットマップが他の型と異なる理由は、描画先の出力ではなく、入力データのソースであるためです。 したがって、ビットマップの重要な点は、その出力が最終的に行われる DPI ではなく、ソース イメージの DPI であり、現在の表示設定とは全く関係ありません。

100 x 100 の既定の DPI ビットマップを読み込んでレンダーターゲットに描画すると、ビットマップは 96 DPI (100 ピクセル) の 100 DIP から、宛先レンダーターゲットの DPI で 100 DIP にスケーリングされます (高 DPI レンダー ターゲットの場合は、ピクセル数が多くなる可能性があります)。 結果の画像のサイズは常に 100 DIP になります (そのため、不快なレイアウトの驚きはありません)。ただし、DPI の低いソース ビットマップが高い DPI 変換先にスケールアップされた場合、多少のぼやけが発生する可能性があります。

高 DPI で最大限に明確にするために、一部のアプリケーションでは、さまざまな解像度で複数のビットマップ イメージセットを提供し、読み込み時に、ターゲット コントロールの DPI に最も近いバージョンを選択する必要があります。 他のアプリでは、高 DPI ビットマップのみを出荷し、低 DPI ディスプレイで実行しているときに Win2D でこれらをスケールダウンすることを好む場合があります (スケールアップよりもスケールダウンの方が見た目が良い場合があります)。 いずれの場合も、 LoadAsync(ICanvasResourceCreator, String, Single)するパラメーターとしてビットマップ DPI を指定できます。

一部のビットマップ ファイル形式には独自の DPI メタデータが含まれていますが、Win2D では正しく設定されないことがよくあります。 代わりに、ビットマップを読み込むときに DPI を明示的に指定する必要があります。

CanvasDrawingSession DPI

CanvasDrawingSession は、描画されているコントロール、レンダーターゲット、スワップチェーンなどから DPI を継承します。

既定では、すべての描画操作は DIP で動作します。 ピクセル単位で作業する場合は、 Units プロパティを使用して変更できます。

効果 DPI

イメージ効果パイプラインは、効果が描画されている CanvasDrawingSession から DPI を継承します。 内部的には、エフェクト処理は常にピクセル単位で動作します。 サイズや位置などのパラメーター値は DIP で指定されますが、これらの単位は実際の画像操作が行われる前にピクセルに変換されます。

ターゲット描画セッションとは異なる DPI のビットマップをエフェクト ソース イメージとして使用すると、ビットマップと効果の間に内部 DpiCompensationEffect が自動的に挿入されます。 これにより、ビットマップがターゲット DPI に合わせてスケーリングされます。これは通常、目的の DPI です。 必要でない場合は、 DpiCompensationEffect の独自のインスタンスを挿入して動作をカスタマイズできます。

Note

カスタム効果を実装する場合は、同等の DPI 処理スキームを適用して、組み込みの Win2D 効果と共に使用する場合の動作が一貫していることを確認することを検討してください。

コンポジション API

Microsoft.Graphics.Canvas.Composition API は Win2D XAML コントロールよりも低いレベルで動作するため、ユーザーに代わって DPI を自動的に処理しようとはしません。 操作する単位を決定し、コンポジション ビジュアル ツリーの一部としてそれを実現するために必要な変換を設定する必要があります。

Windows.UI.CompositionCreateDrawingSurfaceなどの API では、常にピクセル単位でサイズを指定します。 Win2D を使用してコンポジション サーフェイスに描画する場合は、 CreateDrawingSession(CompositionDrawingSurface, Rect, Single)を呼び出すときに使用する任意の DPI を指定できます。 返された CanvasDrawingSession を介して実行されたすべての描画は、それに応じてスケールアップまたはスケールダウンされます。

DPI 処理をテストする方法

表示 DPI の変更に応じてアプリが正しいことをテストする最も簡単な方法は、Windows 10 または Windows 11 で実行し、アプリの実行中に表示設定を変更することです。

  • デスクトップの背景を右クリックし、[表示設定] を選択します
  • [テキスト、アプリ、その他の項目のサイズを変更する] というラベルのスライダーを移動します。
  • [適用] ボタンをクリックします
  • [後でサインアウトする] を選択する

Windows 10 または Windows 11 がない場合は、Windows シミュレーターを使用してテストすることもできます。 Visual Studio のツール バーで、[ローカル コンピューター] 設定を [シミュレーター] に変更し、[解決の変更] アイコンを使用して、シミュレートされた表示を次の間で切り替えます。

  • 100% (DPI = 96)
  • 140% (DPI = 134.4)
  • 180% (DPI = 172.8)