When animating
(scaling) a complex WPF vector brush, 100% of my CPU is used. The animation
also looks jerky. To speed things up, I rasterized the vector brush into a
bitmap brush. The CPU load decreases below 30% and the animation became much
smoother.
So how do I create
a bitmap brush? There is no meaningful properties or methods to override in the
Brush class, as most of the workings of brush are marked as internal. To
overcome the problem, the brush is implemented as a markup extension.
To use the code,
pass your vector brush into the RasterizeBrush class.
<Button
x:Name="helloButton"
Background="{app:RasterizeBrush
{StaticResource HelloBrush}}"
>
<TextBlock>Hello</TextBlock>
</Button>
And place the
following code inside your project.
/-----------------------------------------------------------------------
// <copyright file="RasterizeBrushExtension.cs"
company="Jeow Li Huan">
// Copyright (c) Jeow Li Huan. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Huan.Windows.Markup
{
using System;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
/// <summary>
/// Converts a vector brush into a bitmap brush to
speed up scaling.
/// </summary>
[MarkupExtensionReturnType(typeof(ImageBrush))]
public class RasterizeBrushExtension : MarkupExtension
{
/// <summary>
/// Object used to synchronize access to the static
properties <see
cref="P:DefaultWidth"/> and <see cref="P:DefaultHeight"/>.
/// </summary>
private static object sync = new object();
/// <summary>
/// Backing field for the <see
cref="P:DefaultWidth"/> property.
/// </summary>
private static int defaultWidth = 64;
/// <summary>
/// Backing field for the <see
cref="P:DefaultHeight"/> property.
/// </summary>
private static int defaultHeight = 64;
/// <summary>
/// Backing field for the <see
cref="P:OriginalBrush"/> property.
/// </summary>
private Brush originalBrush;
/// <summary>
/// The converted bitmap brush.
/// </summary>
private ImageBrush rasteredBrush;
/// <summary>
/// Backing field for the <see cref="P:Width"/> property.
/// </summary>
private int width;
/// <summary>
/// Backing field for the <see cref="P:Height"/> property.
/// </summary>
private int height;
/// <summary>
/// Initializes a new instance of the <see
cref="RasterizeBrushExtension"/> class.
/// </summary>
public RasterizeBrushExtension()
{
}
/// <summary>
/// Initializes a new instance of the <see
cref="RasterizeBrushExtension"/> class.
/// </summary>
/// <param name="originalBrush">The original brush that is to be
converted into a bitmap brush.</param>
public RasterizeBrushExtension(Brush originalBrush)
: this()
{
this.originalBrush = originalBrush;
}
/// <summary>
/// Gets or sets the default number of horizontal
pixels for the bitmap to render on.
/// </summary>
/// <value>The default number of horizontal pixels for the bitmap to render on.</value>
public static int DefaultWidth
{
get
{
lock (sync)
return defaultWidth;
}
set
{
lock (sync)
defaultWidth =
value;
}
}
/// <summary>
/// Gets or sets the default number of vertical pixels
for the bitmap to render on.
/// </summary>
/// <value>The default number of vertical pixels for the bitmap to render on.</value>
public static int DefaultHeight
{
get
{
lock (sync)
return defaultHeight;
}
set
{
lock (sync)
defaultHeight
= value;
}
}
/// <summary>
/// Gets or sets the original brush that is to be
converted into a bitmap brush.
/// </summary>
/// <value>The original brush that is to be converted into a bitmap brush.</value>
[ConstructorArgument("originalBrush")]
public Brush OriginalBrush
{
get
{
return this.originalBrush;
}
set
{
if (this.originalBrush != value)
{
this.rasteredBrush = null;
this.originalBrush = value;
}
}
}
/// <summary>
/// Gets or sets the number of horizontal pixels for
the bitmap to render on.
/// </summary>
/// <value>The number of horizontal pixels for the bitmap to render on.</value>
public int Width
{
get
{
return this.width;
}
set
{
ifthis.width != value)
{
this.rasteredBrush = null;
this.width = value;
}
}
}
/// <summary>
/// Gets or sets the number of vertical pixels for the
bitmap to render on.
/// </summary>
/// <value>The number of vertical pixels for the bitmap to render on.</value>
public int Height
{
get
{
return this.height;
}
set
{
if (this.height != value)
{
this.rasteredBrush = null;
this.height = value;
}
}
}
/// <summary>
/// Returns the converted bitmap brush.
/// </summary>
/// <param name="serviceProvider">Not used.</param>
/// <returns>
/// The converted bitmap brush.
/// </returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
int width = (this.width == 0) ? RasterizeBrushExtension.defaultWidth : this.width;
int height = (this.height == 0) ? RasterizeBrushExtension.defaultHeight : this.height;
if (this.originalBrush == null || width == 0 || height == 0)
return null;
if (this.rasteredBrush == null)
{
RenderTargetBitmap targetBitmap = new RenderTargetBitmap(width, height, 96.0, 96.0, PixelFormats.Default);
Rectangle rectangle = new Rectangle();
rectangle.Width =
width;
rectangle.Height =
height;
rectangle.Fill = this.originalBrush;
rectangle.Measure(new Size(width, height));
rectangle.Arrange(new Rect(0, 0, width, height));
targetBitmap.Render(rectangle);
targetBitmap.Freeze();
this.rasteredBrush = new ImageBrush(targetBitmap);
TileBrush tileBrush = this.originalBrush as TileBrush;
if (tileBrush != null)
{
this.rasteredBrush.AlignmentX = tileBrush.AlignmentX;
this.rasteredBrush.AlignmentY = tileBrush.AlignmentY;
this.rasteredBrush.Stretch = tileBrush.Stretch;
this.rasteredBrush.TileMode = tileBrush.TileMode;
this.rasteredBrush.Viewbox = tileBrush.Viewbox;
this.rasteredBrush.ViewboxUnits = tileBrush.ViewboxUnits;
this.rasteredBrush.Viewport = tileBrush.Viewport;
this.rasteredBrush.ViewportUnits =
tileBrush.ViewportUnits;
}
}
return this.rasteredBrush;
}
}
}
No comments:
Post a Comment