如何在另一个线程上渲染视觉效果

本文关键字:视觉 另一个 线程 | 更新日期: 2023-09-27 18:22:44

我正在尝试在另一个线程上渲染画布。这是我的尝试:

// returns path to exported image
private string exportToImage(double width, double height, Visual visual)
{
    var filename = string.Format(@"{0}.png", Guid.NewGuid());
    var tempFile = Path.Combine(tempDir, filename);
    Rect rect = new Rect(0, 0, width, height);
    RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
        (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
    Thread RENDER_THREAD = new Thread(() => 
    {
        this.Dispatcher.Invoke((Action)(() =>
        {
            rtb.Render(visual);
        }));
    });
    RENDER_THREAD.Start();
    //endcode as PNG
    BitmapEncoder pngEncoder = new PngBitmapEncoder();
    pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
    //save to memory stream
    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    pngEncoder.Save(ms);
    ms.Close();
    System.IO.File.WriteAllBytes(tempFile, ms.ToArray());
    return tempFile;
}

我没有得到任何错误,但渲染的结果是一个空的图像。rtb在主线程和新线程中的实例是否相同?

编辑:这是我最近的一次尝试。

我在MouseUp事件上创建线程:

var curLayer = GetItemsPanel((canvasDataBinding.ItemContainerGenerator.ContainerFromIndex(Binding_LayersListView.SelectedIndex)));
Thread RASTERIZE_THREAD = new Thread(() => { exportToImage(curLayer.ActualWidth, curLayer.ActualHeight, curLayer); });
RASTERIZE_THREAD.Start();

这是我的新线程使用的方法:

void exportToImage(double width, double height, Visual visual)
{
    var filename = string.Format(@"{0}.png", Guid.NewGuid());
    var tempFile = Path.Combine(tempDir, filename);
    Rect rect = new Rect(0, 0, width, height);
    RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
        (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
    this.Dispatcher.Invoke(new Action(() =>
    {
        rtb.Render(visual);
    }));
    //endcode as PNG
    BitmapEncoder pngEncoder = new PngBitmapEncoder();
    pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
    //save to memory stream
    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    pngEncoder.Save(ms);
    ms.Close();
    System.IO.File.WriteAllBytes(tempFile, ms.ToArray());
}

那么,为什么它告诉我调用线程不能访问这个对象,因为rtb.Render(visual)上有另一个线程拥有它呢?我没有从其他地方调用exportToImage,那么为什么Dispatcher没有与我创建的线程关联呢?

编辑:我需要在Dispatcher.Invoke().中创建RebderTargetBitmap

这里有一段代码,说明如何在线程中呈现相同的内容并使其异步

整个想法是

  • 将可视化序列化为xaml字符串
  • 创建STA线程
  • 然后让线程对其进行反序列化
  • 在内容控件中托管反序列化的可视化,并执行排列等操作
  • 最后但并非最不重要的是,渲染它(取自代码)

该代码不依赖于Dispatcher,因此它能够运行完全异步

    void exportToImage(double width, double height, Visual visual)
    {
        //this line can not be inside a thread because different thread owns the visual
        string visualData = XamlWriter.Save(visual);
        Thread t = new Thread(() =>
            {
                var filename = string.Format(@"{0}.png", Guid.NewGuid());
                var tempFile = System.IO.Path.Combine("c:''", filename);
                Rect rect = new Rect(0, 0, width, height);
                //create a host control to host the visual
                ContentControl cc = new ContentControl();
                cc.Content = XamlReader.Parse(visualData);
                //call Arrange to let control perform layout (important)
                cc.Arrange(rect);
                RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
                    (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
                rtb.Render(cc);
                //endcode as PNG
                BitmapEncoder pngEncoder = new PngBitmapEncoder();
                pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
                //save to memory stream
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                pngEncoder.Save(ms);
                ms.Close();
                System.IO.File.WriteAllBytes(tempFile, ms.ToArray());
            });
        //STA is necessary for XamlReader.Parse, and proper rendering
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }

我已经成功地测试了这个代码来呈现一些测试视觉效果。但结果可能会因使用方式而异。作为猜测绑定&光栅图像可能有一些问题,但在我的测试中,它就像一个魅力。

如何在另一个线程上渲染视觉效果