【问题标题】:Pixel Shader Render Image [duplicate]像素着色器渲染图像 [重复]
【发布时间】:2013-12-04 11:02:07
【问题描述】:

谁能帮我解决这个问题...

我正在尝试创建一个具有多线程处理图像的单个窗口。

如果我为每个处理操作启动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)
时间 System.Windows.Media.Effects.PixelShader.ManualUpdateResource(通道 频道,布尔型 skipOnChannelCheck) em System.Windows.Media.Effects.PixelShader.UpdateResource(通道 频道,布尔型 skipOnChannelCheck) em System.Windows.Media.Effects.PixelShader.System.Windows.Media.Composition.DUCE.IResource.AddRefOnChannel(通道 频道)时间 System.Windows.Media.Effects.ShaderEffect.AddRefOnChannelCore(通道 频道)时间 System.Windows.Media.Effects.Effect.System.Windows.Media.Composition.DUCE.IResource.AddRefOnChannel(通道 频道)em System.Windows.Media.Visual.UpdateEffect(频道 通道、ResourceHandle 句柄、VisualProxyFlags 标志、布尔值 isOnChannel) 时间 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,通道通道,视觉视觉,Int32 宽度,Int32 高度,双 dpiX,双 dpiY,矩阵 worldTransform,矩形 windowClip) 时间 System.Windows.Media.Imaging.BitmapVisualManager.Render(视觉视觉, 矩阵 worldTransform, Rect windowClip) em System.Windows.Media.Imaging.BitmapVisualManager.Render(视觉)

【问题讨论】:

    标签: c# .net wpf multithreading exception


    【解决方案1】:

    这里的技巧是冻结位图图像并将其传递回 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);
    }
    

    如果它不起作用,那么您需要将 Task.Factory.StartNew 替换为“new Thread()”构造并将单元状态设置为 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);
    

    }

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-21
    • 1970-01-01
    相关资源
    最近更新 更多