在 WPF 中,大多数控件都有一个Effect
属性,例如Grid.Effect
允许创建模糊网格。我想看看如何在 WinRT 中创建背景模糊的弹出窗口?
问问题
1013 次
3 回答
2
XAML 中的 WinRT 不支持效果。您可以使用 DirectX 互操作并使用 C++ (SwapChainPanel) 将着色器放在那里。
于 2013-10-25T21:20:43.360 回答
2
正如您所注意到的,Effect
s 目前在 WinRT 中不可用。
想法:
- 我建议考虑不要创建模糊的背景效果,因为它不会在 Windows 应用商店应用程序中提供一致的体验。
Effect
s,至少在 Widows 应用商店中的“设计语言”并不是一个很好的匹配。 - 或者,您可以只覆盖带有一些噪点的半透明图像,以至少给人一种不均匀的效果(而不仅仅是使部分透明的纯色)。
- 或者,也许只是将背景“变暗”(同样,叠加层会让这很容易)以提供更“灯箱”风格的效果。
- 虽然您可能能够使用互操作,但这可能会显着增加您的应用程序的复杂性,尤其是当它用于诸如此类的简单功能时。
于 2013-10-26T01:38:38.520 回答
2
不这样做或使用 DirectX 的替代方法可能是将新RenderTargetBitmap
类与RenderAsync()
允许您从 C# 或 VB 级别访问控件像素的方法一起使用(假设这是您正在使用的),这可能更容易比 DirectX 快,并且实际上足够快,可以在 CPU 上运行一些基本效果。我FxContentControl
上周末在 WinRT XAML Toolkit 中完成了它,它似乎在一些简单的场景中工作正常,主要问题是 - 何时更新效果。
该控件当前不模糊任何内容,而是找到内容的扩展像素并将它们变为黑色,作为一个穷人的方式来为TextBlock
具有一些透明和一些非透明区域的 a 或任何其他控件生成笔画。您可以将其更改为模糊或添加选项以相当容易地按需进行。
这是模板化控件的默认样式:
<Style
TargetType="controls:FxContentControl">
<Setter
Property="HorizontalContentAlignment"
Value="Left" />
<Setter
Property="VerticalContentAlignment"
Value="Top" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="controls:FxContentControl">
<Grid>
<Image
x:Name="BackgroundFxImage"
Stretch="None"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<Grid
x:Name="RenderedGrid"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<ContentPresenter
x:Name="ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
Content="{TemplateBinding Content}" />
</Grid>
<Image
x:Name="ForegroundFxImage"
Stretch="None"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这是代码:
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using WinRTXamlToolkit.Imaging;
namespace WinRTXamlToolkit.Controls
{
public class FxContentControl : ContentControl
{
private Image _backgroundFxImage;
private Image _foregroundFxImage;
private ContentPresenter _contentPresenter;
private Grid _renderedGrid;
public FxContentControl()
{
this.DefaultStyleKey = typeof(FxContentControl);
}
protected override async void OnApplyTemplate()
{
base.OnApplyTemplate();
_backgroundFxImage = this.GetTemplateChild("BackgroundFxImage") as Image;
_foregroundFxImage = this.GetTemplateChild("ForegroundFxImage") as Image;
_contentPresenter = this.GetTemplateChild("ContentPresenter") as ContentPresenter;
_renderedGrid = this.GetTemplateChild("RenderedGrid") as Grid;
if (_renderedGrid != null)
{
_renderedGrid.SizeChanged += this.OnContentPresenterSizeChanged;
}
if (_renderedGrid.ActualHeight > 0)
{
await this.UpdateFx();
}
}
private async void OnContentPresenterSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
{
await this.UpdateFx();
}
private async Task UpdateFx()
{
await this.UpdateBackgroundFx();
}
private async Task UpdateBackgroundFx()
{
if (_renderedGrid.ActualHeight < 1 ||
_backgroundFxImage == null)
{
return;
}
var rtb = new RenderTargetBitmap();
await rtb.RenderAsync(_renderedGrid);
var pw = rtb.PixelWidth;
var ph = rtb.PixelHeight;
var wb = _backgroundFxImage.Source as WriteableBitmap;
if (wb == null ||
wb.PixelWidth != pw ||
wb.PixelHeight != ph)
{
wb = new WriteableBitmap(pw, ph);
}
await ProcessContentImage(rtb, wb, pw, ph);
_backgroundFxImage.Source = wb;
}
protected virtual async Task ProcessContentImage(RenderTargetBitmap rtb, WriteableBitmap wb, int pw, int ph)
{
var rtbBuffer = await rtb.GetPixelsAsync();
var rtbPixels = rtbBuffer.GetPixels();
var wbBuffer = wb.PixelBuffer;
var wbPixels = wbBuffer.GetPixels();
// Expand
int expansion = 1;
for (int x = 0; x < pw; x++)
for (int y = 0; y < ph; y++)
{
int x1min = Math.Max(0, x - expansion);
int x1max = Math.Min(x + expansion, pw - 1);
int y1min = Math.Max(0, y - expansion);
int y1max = Math.Min(y + expansion, ph - 1);
//bool found = false;
byte maxa = 0;
for (int x1 = x1min; x1 <= x1max; x1++)
for (int y1 = y1min; y1 <= y1max; y1++)
{
var a = rtbPixels.Bytes[4 * (y1 * pw + x1) + 3];
if (a > maxa)
maxa = a;
}
wbPixels.Bytes[4 * (y * pw + x)] = 0;
wbPixels.Bytes[4 * (y * pw + x) + 1] = 0;
wbPixels.Bytes[4 * (y * pw + x) + 2] = 0;
wbPixels.Bytes[4 * (y * pw + x) + 3] = maxa;
}
wbPixels.UpdateFromBytes();
}
}
}
它还使用以下 IBuffer 包装器:
using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
namespace WinRTXamlToolkit.Imaging
{
// ReSharper disable InconsistentNaming - This class extends IBuffer
/// <summary>
/// Contains extensions for an IBuffer interface in the context of a WriteableBitmap,
/// which exposes one to access its pixels.
/// </summary>
public static class IBufferExtensions
// ReSharper restore InconsistentNaming
{
/// <summary>
/// Gives access to the pixels of a WriteableBitmap given an IBuffer
/// exposed by Pixels property.
/// </summary>
/// <remarks>
/// Note that creating this object copies the pixels buffer
/// into the Bytes byte array for quick pixel access
/// and the array needs to be copied back to the pixels buffer
/// to update the bitmap with a call to UpdateFromBytes().
/// This is acceptable for convenience and possibly best for
/// performance in some scenarios, but it does add some upfront
/// overhead as well overhead to update the bitmap at the end.
/// This is only a theory and for better performance it might be
/// good to test different approaches.
/// The goal of this approach is code simplicity. For best performance
/// using native code and/or DirectX is recommended.
/// </remarks>
public class PixelBufferInfo
{
private readonly Stream _pixelStream;
/// <summary>
/// The bytes of the pixel stream.
/// </summary>
public byte[] Bytes;
/// <summary>
/// Initializes a new instance of the <see cref="PixelBufferInfo" /> class.
/// </summary>
/// <param name="pixelBuffer">The pixel buffer returned by WriteableBitmap.PixelBuffer.</param>
public PixelBufferInfo(IBuffer pixelBuffer)
{
_pixelStream = pixelBuffer.AsStream();
this.Bytes = new byte[_pixelStream.Length];
_pixelStream.Seek(0, SeekOrigin.Begin);
_pixelStream.Read(this.Bytes, 0, Bytes.Length);
//this.Pixels = bytes.ToPixels();
}
/// <summary>
/// Updates the associated pixel buffer from bytes.
/// </summary>
public void UpdateFromBytes()
{
_pixelStream.Seek(0, SeekOrigin.Begin);
_pixelStream.Write(Bytes, 0, Bytes.Length);
}
}
/// <summary>
/// Gets the pixels access wrapper for a PixelBuffer property of a WriteableBitmap.
/// </summary>
/// <param name="pixelBuffer">The pixel buffer.</param>
/// <returns></returns>
public static PixelBufferInfo GetPixels(this IBuffer pixelBuffer)
{
return new PixelBufferInfo(pixelBuffer);
}
}
}
于 2013-10-29T04:29:27.410 回答