【问题标题】:Projection is not being respected when rendering a UIElement into a WriteableBitmap将 UIElement 呈现为 WriteableBitmap 时不考虑投影
【发布时间】:2011-11-16 12:23:27
【问题描述】:

我正在使用 Silverlight 4,但无法从 UIElements 正确生成应用了投影的位图。

在我的特定情况下,我使用WriteableBitmap 类来捕获Image 控件的位图,该控件应用了Matrix3DProjection。该投影表示非仿射变换,用于将图像变换为任意四边形。然后保存生成的位图供以后使用。

不幸的是,当WriteableBitmap 捕获UIElement 时,投影似乎被忽略了。这似乎也适用于任何可能附加的 RenderTransform,尽管这可以通过在接受 Transform 的 WriteableBitmap 构造函数上使用重载来解决。似乎没有对预测进行过这样的考虑。

以下人为设计的代码示例说明了这一点。它转换了Button而不是Image组件,但效果是一样的。

XAML:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Button x:Name="button" Grid.Column="0" Width="100" Height="50" Click="button_Click">Render me</Button>
    <Image x:Name="image" Grid.Column="1" Width="200" Height="200" Stretch="Uniform"/>
</Grid>

背后的代码:

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();

        button.Projection = new Matrix3DProjection()
        {
            ProjectionMatrix = new Matrix3D()
            {
                M11 = 0.825, M12 = -0.513, M13 = 0, M14 = 0.001,
                M21 = 0.023, M22 = 0.986, M23 = 0, M24 = -0.002,
                M31 = 0, M32 = 0, M33 = 1, M34 = 0,
                OffsetX = 0, OffsetY = 0, OffsetZ = 0, M44 = 1
            }
        }; 
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        // Render the button as a bitmap and display
        WriteableBitmap bitmap = new WriteableBitmap(button, null);
        image.Source = bitmap;
    }
}

运行时,您会发现在Image组件中渲染了一个按钮的图像,但没有原始按钮本身明显的失真。

我尝试过在WriteableBitmap 上使用构造函数和Render 方法,但它们所有的各种重载都没有成功。我还尝试将源 UIElement 嵌套在其他元素中,并在树的不同级别应用投影,以查看 WriteableBitmap 是否会正确呈现投影,如果它是更大复合结构的一部分。那里也没有运气。

希望我只是在这里遗漏了一些简单的东西,但我怀疑这在当前版本的 Silverlight 中可能是不可能的。

如果这确实是一个死胡同,那么对我来说一个可行的替代方案是能够直接将矩阵变换应用于位图,完全绕过整个 UIElement.Projection 的silverlight。但是,我真的不知道从哪里开始获取源位图,通过这种转换输入它的像素,并收集结果输出。变换是非仿射的这一事实向我表明,将涉及某种程度的插值,并且这种解决方案的数学要求可能很重要。

非常感谢任何帮助或建议。

【问题讨论】:

    标签: silverlight silverlight-4.0 uielement writeablebitmap


    【解决方案1】:

    尝试使用 Silverlight Control Toolkit(System.Windows.Controls.Layout.Toolkit 程序集)中的LayoutTransformer

    <UserControl x:Class="SilverlightApplication1.MainPage"
        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"
        mc:Ignorable="d"
                 xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
        d:DesignHeight="300" d:DesignWidth="400">
    
        <Canvas x:Name="LayoutRoot" Background="White">
            <toolkit:LayoutTransformer x:Name="lt" >
                <Button x:Name="button" Click="Button_Click">Click me</Button>
            </toolkit:LayoutTransformer>
    
            <Border BorderBrush="Red" BorderThickness="1" Canvas.Left="100" Canvas.Top="100">
                <Image x:Name="image"  Width="100" Height="100" />
            </Border>
        </Canvas>
    </UserControl>
    

    后面的代码:

        public MainPage()
        {
            InitializeComponent();
            button.Projection = new Matrix3DProjection() { ProjectionMatrix = new Matrix3D() { M11 = 0.825, M12 = -0.513, M13 = 0, M14 = 0.001, M21 = 0.023, M22 = 0.986, M23 = 0, M24 = -0.002, M31 = 0, M32 = 0, M33 = 1, M34 = 0, OffsetX = 0, OffsetY = 0, OffsetZ = 0, M44 = 1 } };
        }
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            WriteableBitmap bitmap = new WriteableBitmap(lt, null); 
            image.Source = bitmap; 
        }
    

    【讨论】:

    • 像魅力一样工作 - 非常感谢!我仍然不清楚究竟是什么让这个工作,所以我将通过 LayoutTransformer 代码,看看我是否能弄清楚。完成后我会将我的发现发回这里。
    • 嗯 - 看起来我最初的实验并不像我想象的那样彻底地将源元素嵌套在其他控件中。事实证明,将投影应用于子级,但从父级渲染到 WriteableBitmap 可以按预期工作。 LayoutTransformer 有效,但不是必需的。一个简单的父边框将做同样的伎俩。我会将此答案保留为已接受的答案,但会添加另一个包含相关详细信息的答案。再次感谢。
    【解决方案2】:

    解决我的问题的方法很简单。只需将源UIElement 设为另一个的子元素,然后将投影应用于子元素,但在渲染到WriteableBitmap 时使用父元素。修改后的代码示例如下:

    XAML:

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Border x:Name="border" Width="200" Height="200" Grid.Column="0">
            <Button x:Name="button" Width="120" Height="50" Click="button_Click">Render me</Button>
        </Border>
        <Image x:Name="image" Grid.Column="1" Width="200" Height="200"/>
    </Grid>
    

    背后的代码:

    public MainPage()
    {
        InitializeComponent();
    
        button.Projection = new Matrix3DProjection()
        {
            ProjectionMatrix = new Matrix3D()
            {
                M11 = 0.825, M12 = -0.513, M13 = 0, M14 = 0.001,
                M21 = 0.023, M22 = 0.986, M23 = 0, M24 = -0.002,
                M31 = 0, M32 = 0, M33 = 1, M34 = 0,
                OffsetX = 0, OffsetY = 0, OffsetZ = 0, M44 = 1
            }
        }; 
    }
    
    private void button_Click(object sender, RoutedEventArgs e)
    {
        // Render the button as a bitmap and display
        WriteableBitmap bitmap = new WriteableBitmap(border, null);  
        image.Source = bitmap;
    }
    

    感谢 @foson 让我更深入地了解这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-28
      • 1970-01-01
      • 1970-01-01
      • 2015-12-25
      • 1970-01-01
      • 2015-08-26
      相关资源
      最近更新 更多