【WPF学習中】Bitmapにピクセル単位で描画2「直線を引く」

コンピュータ
WPFのBitmapオブジェクトへ直線を描画してみます。 ピクセル単位での描画する方法は調べてありますので、「直線を引く」ぐらいはプログラムならば簡単に出来るだろうと考えていました。
【WPF学習中】Bitmapにピクセル単位で描画
WPFでBitmap画像に対しピクセル単位で描画する方法を探したところ、byte配列でピクセルデータを用意し、そのbyte配列をWriteableBitmapオブジェクトに書き込む方法が見つかりましたので試してみます。 プロジェクトの作成 ...
「直線を引く」アルゴリズムを考えてみて1分ぐらいで自分の手に余ると断念しました。 ネット検索したところWikipediaに直線を引くアルゴリズムがありましたので、C#で書き直してみました。
ブレゼンハムのアルゴリズム - Wikipedia

プロジェクトの作成

PowerShellで実行。要dotnet.exe
mkdir DrawLine
cd DrawLine
dotnet new wpf
code .

ソースコード

<Window x:Class="DrawLine.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:DrawLine"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Image x:Name="image1" Stretch="None" />
    </Grid>
</Window>
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace DrawLine
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        static void DrawLine(ref WriteableBitmap bitmap, int x0, int y0, int x1, int y1, Color c)
        {
            int width = (int)bitmap.PixelWidth;
            int height = (int)bitmap.PixelHeight;
            int pixelsSize = (width * 4) * height;
            byte[] pixels = new byte[pixelsSize];

            int stride = (width * bitmap.Format.BitsPerPixel + 7) / 8;
            bitmap.CopyPixels(pixels, stride, 0);

            bitmap.Lock();

            int dx = System.Math.Abs(x1-x0);
            int dy = System.Math.Abs(y1-y0);
            int sx = (x0 < x1) ? 1 : -1;
            int sy = (y0 < y1) ? 1 : -1;
            int err = dx - dy;

            while(true)
            {
                // System.Console.WriteLine("x:{0}y:{1}", x0, y0);
                
                int i = (x0 * 4) + (y0 * (width * 4));
                pixels[i+0] = (byte)c.B;
                pixels[i+1] = (byte)c.G;
                pixels[i+2] = (byte)c.R;
                pixels[i+3] = (byte)c.A;

                if ((x0 == x1) && (y0 == y1)) break;
                int e2 = 2 * err;
                if (e2 > dy)
                {
                    err = err - dy;
                    x0 = x0 + sx;
                }
                if (e2 < dx)
                {
                    err = err + dx;
                    y0 = y0 + sy;
                }
            }
            bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0, 0);

            bitmap.Unlock();
        }
        public MainWindow()
        {
            InitializeComponent();

            const int width = 256;
            const int height = 256;

            var wb = new WriteableBitmap(width, height, 75, 75, PixelFormats.Bgra32, null);

            int x0 = 16;
            int x1 = 240;
            int y0 = 240;
            int y1 = 16;
            DrawLine(ref wb, x0, y0, x1, y1, Color.FromArgb(255, 255, 0, 0));


            image1.Source = wb;
        }
    }
}

ビルド

dotnet build

実行

dotnet run
作っては見ましたが、WPFの標準機能でbitmapへ直線を引く機能を見つけることが出来ないことを考えると、WPFではラスター画像で図形を描画するのではなくベクター画像を使えと言う事かもしれません。
ベクター画像としてパスで直線を描画する方法はこちら。
【WPF学習中】Pathで動的に直線を引く
調べたところWPFで直線を引こうと思ったらXAMLでPathで静的に直線を引くことが出来るようです。 こちらを参考に、マウスのクリックイベントで直線の始点と終点を拾って、動的に直線を引く方法を考えてみました。 MVVMぽくプログラミングしよ...

コメント