LoginSignup
3
3

More than 3 years have passed since last update.

【C#/WPF】WriteableBitmapで点を打って、テレビの放送終了後の砂嵐をつくる(タイマーで処理)

Last updated at Posted at 2019-04-22

やりたいこと

任意の場所に点を打って、画像を作成したい。

WriteableBitmapを使う

WriteableBitmapは、BitmapSourceクラスから派生したクラス。
他にBitmapSourceから派生したクラスに、BitmapImage、RenderTargetBitmap、TransformedBitmapなどがある。(msdocs/自分のページ)

それぞれ派生クラスには役割があるが、WriteableBitmapは、ピクセル単位で任意の点を打つことができる。
想像するに、写真やwebカメラ画像の上にWriteableBitmapで書いた透明の領域と半透明の黒い領域をを含む画像をかぶせて、「無効領域」などを表現したりできそう。

試しに作成したサンプル(ViewModelのみ抜粋)

試しに、掲題の「砂嵐」を出すサンプルを作成した。

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace WpfApp1
{
    class ViewModel : BindingBase
    {        
        public WriteableBitmap MyWBitmap { get; set; } = new WriteableBitmap(1280, 720, 96, 96, PixelFormats.Pbgra32, null);

        DispatcherTimer _drawingTimer = new DispatcherTimer();

        public ViewModel()
        {
            // 描画タイマー作動
            _drawingTimer.Interval = new TimeSpan(0, 0, 0, 0, 33);
            _drawingTimer.Tick += _timer_draw_Tick;
            _drawingTimer.Start();
        }

        // 画面の更新はメインスレッドで実施しないといけないので、Task等ではできない。
        // (具体的には、MyWBitmapに値を入れるところを別スレッドにはできない)
        private void _timer_draw_Tick(object sender, EventArgs e)
        {
            int width = (int)1280;
            int height = (int)720;

            // 計算用のバイト列の準備
            int pixelsSize = (int)(width * height * 4);
            byte[] pixels = new byte[pixelsSize];

            // 乱数の準備
            Random rnd = new System.Random();
            int rndMax = 256;                    // 0~256の乱数を取得

            // バイト列に色情報を入れる
            for (int i = 0; i < width * height; i++)
            {
                pixels[4 * i] = (byte)rnd.Next(rndMax);        //blue;
                pixels[4 * i + 1] = (byte)rnd.Next(rndMax);    // green;
                pixels[4 * i + 2] = (byte)rnd.Next(rndMax);    // red;
                pixels[4 * i + 3] = (byte)255;                 //alpha
            }

            // バイト列をBitmapImageに変換する
            int stride = width * 4; // 一行あたりのバイトサイズ(Pbgra32だから、1ピクセル当たり4バイトとなるので、ピクセル数に4を掛ける)
            MyWBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, stride, 0, 0);
        }
    }
}

ViewModelの概要

  • コードの「// バイト列に色情報を入れる」のところで、ピクセルのデータを作成している。forループ1回分が、1ピクセルのイメージ。
  • 1ピクセル当たり4バイトを使っていて、それぞれ前から[青][緑][赤][アルファ]を示す。
  • なので、透明の領域を作りたい場合は、各ピクセルの4バイト目のアルファ値を0にすればよい。
  • ((未検証)各ピクセルが4バイトなのは、PixelFormats.Pbgra32の場合で、ほかの形式を指定した場合は1ピクセル当たりのバイト数が変わるっぽい(こちら参照)
  • 作ったバイト列を作ったWriteableBitmapのWritePixelsメソッドに渡せば、画像が更新される。
  • 砂嵐は、byte列に値を入れるときに、乱数を入れるだけで作れる。

Xaml側

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Viewbox Grid.Row="0" Grid.Column="1">
            <Image Source="{Binding MyWBitmap}"/>
        </Viewbox>
    </Grid>
</Window>

動かした画面

image.png

unsafeなし

ここでは、byte配列を普通に作って、それをWritePixelsに渡す形で実現したのでunsafeは出てこなかったが、unsafeにして、byteのポインタを使って実現する方法もあるらしい。
C++のライブラリで作成された画像データのbyte配列をC#で受け取るときにそういうことをしているのを見たことがある。
参考:https://blogs.yahoo.co.jp/gogowaten/15497771.html

Taskで処理したバージョン

こちら
Taskは別スレッドで処理が行われるので、Taskの中でも、UI関連の処理をするときだけUIスレッドに戻すということをしないといけない。

コード

参考

WriteableBitmap Class
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.media.imaging.writeablebitmap?view=netframework-4.8

PixelFormats Class
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.media.pixelformats?view=netframework-4.8

C#で画像を描いてみた(WPFでWritableBitmap編)
https://water2litter.net/gin/?p=984

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3