像素着色器渲染图像

时间:2013-12-04 11:02:07

标签: c# .net wpf multithreading exception

任何人都可以帮我解决这个问题......

我正在尝试使用多线程创建一个窗口来处理图像。

如果我为每个处理操作启动process.exe,我就不会遇到这个问题....

这是我的功能:

    private ImageSource ImageApplyEffect(ImageSource imageSource, Effect effect, Size size)
    {
        DrawingVisual d = new DrawingVisual();

        DrawingContext dc = d.RenderOpen();

        dc.DrawImage(imageSource, new Rect(new Point(0, 0), size));

        d.Effect = effect;

        dc.Close();

        dc = null;

        RenderTargetBitmap frameRenderTargetBitmap2 = new RenderTargetBitmap((int)size.Width,
                                                                             (int)size.Height,
                                                                              1 / 96,
                                                                              1 / 96,
                                                                              PixelFormats.Default);
        frameRenderTargetBitmap2.Render(d);

        d = null;


        BitmapFrame frameBitmapFrame2 = BitmapFrame.Create(frameRenderTargetBitmap2);

        frameRenderTargetBitmap2 = null;

        MemoryStream mStream = new MemoryStream();

        PngBitmapEncoder encoder = new PngBitmapEncoder();

        encoder.Frames.Add(frameBitmapFrame2);

        encoder.Save(mStream);

        BitmapImage bImg = new BitmapImage();
        bImg.BeginInit();
        bImg.StreamSource = mStream;
        bImg.EndInit();

        frameBitmapFrame2 = null;


        ImageSource imgS = bImg;

        encoder = null;

        return imgS;
    }

我得到了这个例外:

调用线程无法访问此对象,因为另一个线程拥有它!

例外:

  

em System.Windows.DependencyObject.GetValue(DependencyProperty dp)
  EM   System.Windows.Media.Effects.PixelShader.ManualUpdateResource(通道   channel,Boolean skipOnChannelCheck)em   System.Windows.Media.Effects.PixelShader.UpdateResource(通道   channel,Boolean skipOnChannelCheck)em   System.Windows.Media.Effects.PixelShader.System.Windows.Media.Composition.DUCE.IResource.AddRefOnChannel(通道   频道)em   System.Windows.Media.Effects.ShaderEffect.AddRefOnChannelCore(通道   频道)em   System.Windows.Media.Effects.Effect.System.Windows.Media.Composition.DUCE.IResource.AddRefOnChannel(通道   频道)em System.Windows.Media.Visual.UpdateEffect(频道   channel,ResourceHandle句柄,VisualProxyFlags标志,布尔值   isOnChannel)em   System.Windows.Media.Visual.RenderRecursive(RenderContext ctx)em   System.Windows.Media.Visual.Render(RenderContext ctx,UInt32   childIndex)em System.Windows.Media.Renderer.Render(IntPtr   pRenderTarget,Channel channel,Visual visual,Int32 width,Int32   高度,双dpiX,双dpiY,矩阵worldTransform,Rect   windowClip)em   System.Windows.Media.Imaging.BitmapVisualManager.Render(Visual visual,   Matrix worldTransform,Rect windowClip)em   System.Windows.Media.Imaging.BitmapVisualManager.Render(Visual visual)

1 个答案:

答案 0 :(得分:0)

这里的技巧是冻结位图图像并将其传递回UI线程。此外,某些对象需要序列化/反序列化,以便您可以将它们传递给新线程

    public class Serializer
{
    public static Stream Serialize<T>(T t)
    {
        var memoryStream = new MemoryStream();
        var serializer = new BinaryFormatter();
        serializer.Serialize(memoryStream, t);

        return memoryStream;
    }

    public static T Deserialize<T>(Stream stream)
    {
        var formatter = new BinaryFormatter();
        stream.Seek(0, SeekOrigin.Begin);

        return (T)formatter.Deserialize(stream);
    }
}

public Task<BitmapImage> ApplyImageEffect(ImageSource imageSource, Effect effect, Size size)
{
    var imgSourceSerialized = Serializer.Serialize(imageSource);
    var effectSerialized = Serializer.Serialize(effect);

    return Task.Factory.StartNew(() =>
    {
        var imgSourceDeserialized = Serializer.Deserialize<ImageSource>(imgSourceSerialized);
        var effectDeserialized = Serializer.Deserialize<Effect>(effectSerialized);

        var drawingVisual = new DrawingVisual();
        var drawingContext = drawingVisual.RenderOpen();

        drawingContext.DrawImage(imgSourceDeserialized, new Rect(new Point(0, 0), size));
        drawingVisual.Effect = effectDeserialized;
        drawingContext.Close();

        var frameRenderTargetBitmap2 = new RenderTargetBitmap((int)size.Width,
                                                                             (int)size.Height,
                                                                              1 / 96,
                                                                              1 / 96,
                                                                              PixelFormats.Default);
        frameRenderTargetBitmap2.Render(drawingVisual);

        var frameBitmapFrame2 = BitmapFrame.Create(frameRenderTargetBitmap2);

        var memoryStream = new MemoryStream();
        var encoder = new PngBitmapEncoder();

        encoder.Frames.Add(frameBitmapFrame2);
        encoder.Save(memoryStream);

        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memoryStream;
        bitmapImage.EndInit();

        // important!
        bitmapImage.Freeze();

        return bitmapImage;
    });
}


public MainWindow()
{
    InitializeComponent();


    Task<BitmapImage> [] tasks = new[]
    {
        ApplyImageEffect(new BitmapImage(), new BlurEffect(), new Size(20, 20)),
        ApplyImageEffect(new BitmapImage(), new BlurEffect(), new Size(20, 20))
    };

    Task.WaitAll(tasks);
}

如果它不起作用,那么你需要用“new Thread()”构造替换Task.Factory.StartNew并将公寓状态设置为STA。

我曾写过一些旧代码,这种代码证明了这一概念:

/// <summary>
/// Creates UI element on a seperate thread and transfers it to
/// main UI thread. 
/// 
/// Usage; if you have complex UI operation that takes a lot of time, such as XPS object creation.
/// </summary>
/// <param name="constructObject">Function that creates the necessary UIElement - will be executed on new thread</param>
/// <param name="constructionCompleted">Callback to the function that receives the constructed object.</param>
public void CreateElementOnSeperateThread(Func<UIElement> constructObject, Action<UIElement> constructionCompleted)
{
    VerifyAccess();

    // save dispatchers for future usage.
    // we create new element on a seperate STA thread
    // and then basically swap UIELEMENT's Dispatcher.
    Dispatcher threadDispatcher = null;
    var currentDispatcher = Dispatcher.CurrentDispatcher;

    var ev = new AutoResetEvent(false);
    var thread = new Thread(() =>
        {
            threadDispatcher = Dispatcher.CurrentDispatcher;
            ev.Set();

            Dispatcher.Run();
        });

    thread.SetApartmentState(ApartmentState.STA);
    thread.IsBackground = true;
    thread.Start();
ev.WaitOne();

threadDispatcher.BeginInvoke(new Action(() =>
    {
        var constructedObject = constructObject();
        currentDispatcher.BeginInvoke(new Action(() =>
            {
                var fieldinfo = typeof (DispatcherObject).GetField("_dispatcher",
                                                                   BindingFlags.NonPublic |
                                                                   BindingFlags.Instance);
                if (fieldinfo != null)
                    fieldinfo.SetValue(constructedObject, currentDispatcher);

                constructionCompleted(constructedObject);
                threadDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
            }), DispatcherPriority.Normal);
    }), DispatcherPriority.Normal);

}