読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

ぼかした画像のBrushを作るマークアップ拡張

C# WPF

以前↓で書いた、ぼかしを強烈にかけた画像を高速にする方法ですが、
WPFで強めのブラーを高速にかける - SourceChord
これを、Brushとして使えるようにしてみました。


マークアップ拡張も作ったので、以下のようにImageBrushを使うような感覚でぼかしの効いた背景を作ることができます。
f:id:minami_SC:20140223164858j:plain

ぼかした画像を張り付けるVisualBrushの作成

まずは、前回やったのと同じようなものを、VisualBrushとして作ります。

        <Rectangle Margin="50" Stroke="Black">
            <Rectangle.Fill>
                <VisualBrush Stretch="UniformToFill">
                    <VisualBrush.Visual>
                        <ContentControl>
                            <ContentControl.CacheMode>
                                <BitmapCache RenderAtScale="0.05" />
                            </ContentControl.CacheMode>
                            <Image ClipToBounds="True"
                                   Source="Image/sample.jpg">
                                <Image.Effect>
                                    <BlurEffect Radius="100" />
                                </Image.Effect>
                            </Image>
                        </ContentControl>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Rectangle.Fill>
        </Rectangle>

結果はこんな感じで、前回の記事のように、BitmapCacheの効いた高速なぼかし処理ができています。
f:id:minami_SC:20140223164909j:plain

基本的に前回方法と同じで、BitmapCacheを組み合わせたImageコントロールに、BlurEffectをかけてるだけです。
ちなみに、ImageコントロールをClipToBounds="True"としてるところがミソ
ぼかしをかけて、外側に大きくなるので、外側にぼけて広がった部分をクリッピングしておきます。


ぼかし画像のVisualBrushを生成するマークアップ拡張を作る

で、こんなに長々とVisualBrushの定義をするのは面倒なので、普通のImageBrushと同じような感覚で使えるように、マークアップ拡張を作りました。

ぼかした画像のBrushを生成するマークアップ拡張
    [MarkupExtensionReturnType(typeof(VisualBrush))]
    public class BluredImageBrushExtension : MarkupExtension
    {
        public ImageSource Source { get; set; }
        public Stretch Stretch { get; set; }

        public double BlurRadius { get; set; }
        public double RenderAtScale { get; set; }
        

        public BluredImageBrushExtension()
        {
            Source = null;
            Stretch = System.Windows.Media.Stretch.UniformToFill;
            BlurRadius = 100;
            RenderAtScale = 0.05;
        }

        public BluredImageBrushExtension(ImageSource source, Stretch stretch, double blurRadius, double renderAtScale)
        {
            Source = source;
            Stretch = stretch;
            BlurRadius = blurRadius;
            RenderAtScale = renderAtScale;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            // ぼかしをかけたImageコントロールを作成
            var img = new Image() {
                Source = this.Source,
                ClipToBounds = true,
                Effect = new BlurEffect() { Radius = this.BlurRadius}
            };
            // Imageコントロールを、BitmapCacheを設定したContentControlに配置
            var visual = new ContentControl() {
                Content = img,
                CacheMode = new BitmapCache(this.RenderAtScale)
            };
            // ↑のContentControlを使って、VisualBrushを作成
            var brush = new VisualBrush(visual) { Stretch = this.Stretch };

            return brush;
        }
    }
        <Rectangle Margin="50"
                   Fill="{local:BluredImageBrush Source=Image/sample.jpg,
                                                 Stretch=UniformToFill,
                                                 BlurRadius=100,
                                                 RenderAtScale=0.05}"
                   Stroke="Black" />

まとめ

今回のサンプルコードは以下の通り。
マークアップ拡張は、面倒だったのでMainWindow.xaml.csにそのまま書いてます。

MainWindow.xaml
<Window x:Class="WpfBaseTemplate1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfBaseTemplate1"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <Rectangle Margin="50"
                   Fill="{local:BluredImageBrush Source=Image/sample.jpg,
                                                 Stretch=UniformToFill,
                                                 BlurRadius=100,
                                                 RenderAtScale=0.05}"
                   Stroke="Black" />
    </Grid>
</Window>
MainWindow.xaml.cs
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    [MarkupExtensionReturnType(typeof(VisualBrush))]
    public class BluredImageBrushExtension : MarkupExtension
    {
        public ImageSource Source { get; set; }
        public Stretch Stretch { get; set; }

        public double BlurRadius { get; set; }
        public double RenderAtScale { get; set; }
        

        public BluredImageBrushExtension()
        {
            Source = null;
            Stretch = System.Windows.Media.Stretch.UniformToFill;
            BlurRadius = 100;
            RenderAtScale = 0.05;
        }

        public BluredImageBrushExtension(ImageSource source, Stretch stretch, double blurRadius, double renderAtScale)
        {
            Source = source;
            Stretch = stretch;
            BlurRadius = blurRadius;
            RenderAtScale = renderAtScale;
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            // ぼかしをかけたImageコントロールを作成
            var img = new Image() {
                Source = this.Source,
                ClipToBounds = true,
                Effect = new BlurEffect() { Radius = this.BlurRadius}
            };
            // Imageコントロールを、BitmapCacheを設定したContentControlに配置
            var visual = new ContentControl() {
                Content = img,
                CacheMode = new BitmapCache(this.RenderAtScale)
            };
            // ↑のContentControlを使って、VisualBrushを作成
            var brush = new VisualBrush(visual) { Stretch = this.Stretch };

            return brush;
        }
    }


XAMLの拡張性ってホントすごい!!
改めてXAML面白いと思いました。