Проекция не соблюдается при рендеринге UIElement в WriteableBitmap - PullRequest
1 голос
/ 16 ноября 2011

Я использую Silverlight 4 и у меня возникают проблемы с правильным созданием растровых изображений из UIElements, к которым применена проекция.

В моем конкретном случае я использую класс WriteableBitmap для захвата растрового изображенияImage элемент управления, к которому применено Matrix3DProjection.Эта проекция представляет собой неаффинное преобразование, которое используется для преобразования изображения в произвольный четырехугольник.Полученное растровое изображение затем сохраняется для последующего использования.

К сожалению, проекции, похоже, игнорируются, когда WriteableBitmap фиксирует UIElement.По-видимому, это также относится к любому RenderTransform, который может быть присоединен, хотя это можно устранить с помощью перегрузки в конструкторе WriteableBitmap, который принимает Transform.Похоже, что такие прогнозы не учитывались при прогнозировании.

Следующий пример надуманного кода иллюстрирует это.Он преобразует 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, но без искажений, которые очевидны на самой исходной кнопке.

Я попытался поиграть с обоимиконструктор и метод Render для WriteableBitmap со всеми их различными перегрузками безуспешно.Я также попытался вложить источник UIElement в другие элементы и применить проекцию на различных уровнях дерева, чтобы посмотреть, правильно ли WriteableBitmap отобразит проекцию, если она будет частью большой составной структуры.Там тоже не повезло.

Надеюсь, я просто упускаю что-то простое здесь, но я подозреваю, что это просто невозможно в текущей версии Silverlight.

Если это действительно тупиктогда жизнеспособной альтернативой для меня будет возможность напрямую применить преобразование матрицы к растровому изображению, минуя весь UIElement.Projection кусочек серебряного света в целом.Тем не менее, я не знаю, с чего начать, когда нужно взять исходное растровое изображение, передать его пиксели посредством такого преобразования и собрать полученный результат.Тот факт, что преобразование не является аффинным, подсказывает мне, что в нем будет задействован некоторый уровень интерполяции и что математические требования для такого решения, вероятно, значительны.

Любая помощь или совет очень важны.

Ответы [ 2 ]

0 голосов
/ 17 ноября 2011

Решение моей проблемы оказалось простым. Просто сделайте источник 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 за то, что он заставил меня более внимательно взглянуть на это.

0 голосов
/ 16 ноября 2011

Попробуйте использовать LayoutTransformer из Silverlight Control Toolkit (сборка System.Windows.Controls.Layout.Toolkit)

<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; 
    }
...