WPF アプリケーションのピクセル スナップ
更新 : 2007 年 11 月
WPF のグラフィックス システムでは、解像度やデバイスへの依存を避けるために、デバイスに依存しない独立した単位が使用されます。各デバイス非依存ピクセルは、システムのドット/インチ (dpi) 設定に従って自動的にスケーリングされます。これにより、さまざまな dpi 設定で WPF アプリケーションが適切にスケーリングされ、アプリケーションが自動的に dpi に対応するようになります。
ただし、この dpi に依存しない方法では、アンチエイリアシングによる端の描画にむらが生じる可能性があります。このような不鮮明な表示 (半透明のエッジ) は、端がデバイス ピクセル間ではなく、1 つのデバイス ピクセル内に位置するときに発生する場合があります。この問題に対処するために、WPF では、ピクセル スナップを使用してビジュアル ツリーのオブジェクトの端をデバイス ピクセルにスナップ (つまり固定) し、アンチエイリアシングで生じる半透明のエッジを解消する方法を使用できます。
ピクセル スナップは、ビジュアルのジオメトリに小さいオフセットを適用してジオメトリをデバイス ピクセルに揃えることでこのようなビジュアル アイテムを抑えるための手段です。
このトピックには次のセクションが含まれています。
- アンチエイリアス化されたレンダリングに対するピクセル スナップ
- ガイドライン
- ビットマップ イメージ
- 関連トピック
アンチエイリアス化されたレンダリングに対するピクセル スナップ
鮮明な線
ピクセル スナップを行わないと、エッジがデバイス ピクセルの間にない場合、アンチエイリアス化されてレンダリングされた線が半透明になる可能性があります。次の図は、幅が 1 ピクセルのアンチエイリアス化された線の出力が、デバイス ピクセル内にある場合と (左)、デバイス ピクセルとデバイス ピクセルの間にある場合 (右) を示したものです。
アンチエイリアス化された線のレンダリング
ピクセル スナップを行うと、アンチエイリアス化された線はデバイス ピクセルにスナップし (固定され)、半透明な線のレンダリングが回避されます。次の例は、単一ピクセルの線に対する SnapsToDevicePixels プロパティの効果を示しています。ウィンドウのサイズをゆっくり変更すると、位置が変化する際に、スナップされていない線 (左) ではビジュアル アイテムが発生し、スナップされている線 (右) ではサイズが固定されています。
<Page x:Class="PixelSnapping.Lines"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="Lines" Name="linesPage"
>
<StackPanel Width="150" Margin="7" Orientation="Horizontal">
<!-- Single pixel line with pixel snapping turned OFF.-->
<Rectangle SnapsToDevicePixels="False"
Width="45.5" Margin="10" Height="1" Fill="Red"/>
<!-- Single pixel line with pixel snapping turned ON.-->
<Rectangle SnapsToDevicePixels="True"
Width="45.5" Margin="10" Height="1" Fill="Red"/>
</StackPanel>
<!-- Background Grid -->
<Page.Background>
<DrawingBrush Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Page.Background>
</Page>
メモ : |
---|
SnapsToDevicePixels は、レイアウト パスを通過する要素に対してのみ影響を与えます。DrawingGroup オブジェクトの GuidelineSet プロパティを使用すると、Drawing にガイドラインを設定できます。Visual のガイドラインを手動で設定するには、VisualYSnappingGuidelines プロパティと VisualXSnappingGuidelines プロパティを使用して、新しいガイドラインを作成します。 |
ピクセル スナップにより鮮明になるのは横線と縦線だけであり、斜線には効果がありません。
隣接するオブジェクト
オブジェクト間のエッジが接していて、隣接するエッジがデバイス ピクセルの行または列の間に正確に揃っていない場合も、アンチエイリアス化によってビジュアル アイテムが発生する可能性があります。アンチエイリアス化されたシーンでは、基になっている背景色を使用してエッジが滑らかにされ、各オブジェクトの間のエッジの色が透明になる浸透効果が生成される可能性があります。次の図は、この浸透効果を示したものです。
隣接するオブジェクトでの浸透効果
ピクセル スナップを有効にすると、隣接するエッジがデバイス ピクセルにスナップされ、浸透効果が発生しなくなります。次の例では、隣接するオブジェクトに対する SnapsToDevicePixels プロパティの効果を示します。ウィンドウのサイズをゆっくり変更すると、スナップされていない四角形 (左) では浸透効果が発生しますが、スナップされている四角形 (右) では表示の異常は発生しません。
<Page x:Class="PixelSnapping.Seeping"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="Seeping"
>
<StackPanel Orientation="Horizontal" Height="100">
<Border
SnapsToDevicePixels="False"
Margin="10" BorderThickness="1" BorderBrush="Black" Height="80" Background="White">
<StackPanel Height="100.0">
<Rectangle Width="20" Height="20" Fill="Red"/>
<Rectangle Width="20" Height="20" Fill="Red"/>
<Rectangle Width="20" Height="20" Fill="Red"/>
<Rectangle Width="20" Height="20" Fill="Red"/>
</StackPanel>
</Border>
<Border
SnapsToDevicePixels="True"
Margin="10" BorderThickness="1" BorderBrush="Black" Height="80" Background="White">
<StackPanel Height="100.0">
<Rectangle Width="20" Height="20" Fill="Red"/>
<Rectangle Width="20" Height="20" Fill="Red"/>
<Rectangle Width="20" Height="20" Fill="Red"/>
<Rectangle Width="20" Height="20" Fill="Red"/>
</StackPanel>
</Border>
</StackPanel>
<!-- Background Grid -->
<Page.Background>
<DrawingBrush Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Page.Background>
</Page>
スナップされている四角形では SnapsToDevicePixels プロパティの値を明示的に設定していないことに注意してください。このプロパティは、ルート要素で true に設定するだけで、すべての子要素で動作が有効になります。
テキスト
Windows Presentation Foundation (WPF) は常にアンチエイリアスされたテキストを生成します。生成されたテキストが静的な場合は、ピクセル スナップされます。これにより、グリフがピクセル グリッド上に直接配置されて、アンチエイリアスされたテキストの外観が鮮明になり、テキストがよりクリアになります。ただし、Windows Presentation Foundation (WPF) が、スクロール、スケーリング、アニメーション化された変換など、アニメーション的な動きを検出すると、そのような動きが完了するまでピクセル スナップはオフになります。アニメーションやスクロールの動きが完了すると、ピクセル スナップがゆっくりと復活します。
ガイドライン
基本的に、ピクセル スナップはガイドラインによって制御されます。ガイドラインは、ジオメトリの位置をデバイス ピクセル グリッドに合わせるのに役立ちます。通常は、SnapsToDevicePixels プロパティを使用してピクセル スナップを行うと、意図した結果が得られます。しかし、このプロパティを使用できない場合もあり (特に、Drawing オブジェクトを使用している場合や DrawingContext を直接処理する場合)、ピクセル スナップによって提供される目的の鮮明さを実現するために、ガイドラインの設定が必要になります。
Drawing および DrawingContext オブジェクトにガイドラインを設定するには、GuidelineSet クラスを使用します。このクラスを使用すると、DrawingGroup に適用できる、または以降の描画コマンドのために DrawingContext にプッシュできる、水平および垂直のガイドラインを作成できます。ガイドラインは、どの線をデバイス ピクセルにスナップする必要があるかを描画に指示します。GuidelineSet の使用の詳細な例については、「方法 : 描画に GuidelineSet を適用する」を参照してください。
水平および垂直のガイドライン コレクションを変更することで、ガイドラインを Visual レベルで設定することもできます。これらには、VisualYSnappingGuidelines プロパティおよび VisualXSnappingGuidelines プロパティを使用してアクセスできます。
ビットマップ イメージ
WPF は dpi に依存しないため、ビットマップ ベースの UI は意図したとおりの表示結果にならない場合があります。ピクセルの途中への位置調整の問題により、アンチエイリアス化したシーンではイメージが不鮮明になる場合があります。対照的な要素が隣接する単一ピクセルの線 (白と黒が交互になった線など) のような頻繁な変化を含むイメージでは、これが特に当てはまります。次の図は、揃えられたイメージ (左) と、デバイス ピクセルにオフセットが揃えられていないイメージ (右) での、イメージ品質の違いを示しています。
デバイス ピクセルに合わせたイメージの位置調整
UI のイメージでよくあるシナリオは、アイコンを表すイメージを別のオブジェクトの中央に揃える場合です。通常、アイコンは変化の頻度が高い小さなイメージであるため、アンチエイリアス化によりビジュアル アイテムが生成されるのを防ぐため、アプリケーションのレイアウトに合わせた調整が必要になる場合があります。
イメージを正確に中央に揃えるには、イメージのピクセルの幅または高さが偶数の場合は、コンテナの幅または高さも偶数である必要があります。イメージの幅または高さが奇数ピクセルの場合は、含まれる要素の幅または高さも奇数ピクセルである必要があります。
Image を格納した 2 つの Border オブジェクトを作成する例を次に示します。上の境界の幅と高さは偶数で、イメージと一致しています。下の境界の幅と高さは奇数です。
この例からの出力と、イメージに対するコンテナ サイズの影響を次の図に示します。
<Page x:Class="PixelSnapping.Images"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="Images"
>
<StackPanel>
<!-- Image has a pixel dimension of 144x96. -->
<!-- Because the image has a even width and height,
an even border width and height allows the image to proper center.
-->
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Width="200" Height="100">
<Image HorizontalAlignment="Center" VerticalAlignment="Center" Source="sharpness.png" Stretch="None"/>
</Border>
<!-- Image has a pixel dimension of 144x96. -->
<!-- Because the image has a even width and height,
an odd border width and height causes the image to soften.
-->
<Border HorizontalAlignment="Left" VerticalAlignment="Top" Width="201" Height="101">
<Image HorizontalAlignment="Center" VerticalAlignment="Center" Source="sharpness.png" Stretch="None"/>
</Border>
</StackPanel>
<!-- Grid Background -->
<Page.Background>
<DrawingBrush Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="White">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
<GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Page.Background>
</Page>
残念ながら、コンテナ オブジェクトのサイズを調節するだけでは、デバイス ピクセルの調整は保証されません。アプリケーションのレイアウト全体が、イメージの位置調整に影響を与える場合があります。ディスプレイのドット/インチ (dpi) も、イメージの位置調整に影響します。上の例では、ディスプレイを 96 ドット/インチ (dpi) に設定した場合にのみ、イメージの位置調整が機能します。これ以外の設定では、ディスプレイの設定に合わせてレイアウトを調整する必要があります。Windows Presentation Foundation (WPF) アプリケーションでは、頻繁に変化するイメージは可能な限り避ける必要があります。