[C# WPF] ScrollViewer内にWriteableBitmapで描画してみる
前回に引き続き、もうちょっとWriteableBitmapを使ってみます。
今回はScrollViewerで表示されている部分にだけ、WriteableBitmapで描画を行います。
環境
- Visual Studio 2019
- .NET Framework 4.7.2
ScrollViewerの表示領域にだけ描画してみる
何も考えずにImageを置き換えるとこんな感じになります。
・MainWindow.xaml
<Window x:Class="Sample_WriteableBitmap.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Sample_WriteableBitmap"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ScrollViewer x:Name="xScrollViewer"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"
ScrollChanged="ScrollViewer_ScrollChanged">
<!-- Windowの3倍くらいのCanvasを入れておく -->
<Canvas x:Name="xCanvas"
Height="1800" Width="2400">
<Image x:Name="xImage"/>
</Canvas>
</ScrollViewer>
</Window>
・MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Sample_WriteableBitmap
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DrawImage();
}
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
DrawImage();
}
private void DrawImage()
{
// WritableBitmapを生成する
// ScrollViewerの表示領域ではなくCanvas自体のサイズを指定します
var canvasWidth = (int)xCanvas.Width;
var canvasHeight = (int)xCanvas.Height;
var bitmap = new WriteableBitmap(canvasWidth, canvasHeight, 96, 96, PixelFormats.Pbgra32, null);
// 描画データを格納するバイト列を生成する
// バイト列のサイズはScrollViewerでの表示領域の分だけ作ります
var windowWidth = (int)Width;
var windowHeight = (int)Height;
var size = windowWidth * windowHeight * 4;
var pixels = new byte[size];
// バイト列に描画データを格納する
for (int i = 0; i < size; i += 4)
{
pixels[i] = 255; // Blue
pixels[i + 1] = 0; // Green
pixels[i + 2] = 0; // Red
pixels[i + 3] = 255; // Alpha
}
// スクロール位置に合わせてオフセット距離を算出します
// オフセット後の描画でCanvas領域をはみ出すとクラッシュするのでリミットを掛けます
var destinationX = (int)xScrollViewer.HorizontalOffset;
destinationX = (destinationX < 0) ? 0 : (canvasWidth - windowWidth) < destinationX ? (canvasWidth - windowWidth) : destinationX;
var destinationY = (int)xScrollViewer.VerticalOffset;
destinationY = (destinationY < 0) ? 0 : (canvasHeight - windowHeight) < destinationY ? (canvasHeight - windowHeight) : destinationY;
// バイト列 -> BitmapImage
var stride = windowWidth * 4; // 1行あたりのバイト数
bitmap.WritePixels(new Int32Rect(0, 0, windowWidth, windowHeight), pixels, stride, destinationX, destinationY);
// 転送
xImage.Source = bitmap;
}
}
}
大したことをやっていないのに描画が遅い気がする。
スクロールバーを掴んで動かしているとチラつくときがあります。
WriteableBitmapは、自前でゼロから何かを描く処理には適していないっぽいですね。
画像を読み込んで編集するときには良さそうです。
おしまい。




ディスカッション
コメント一覧
まだ、コメントがありません