X Tutup
Skip to content

OxyPlot SkiaSharp renderer throws a null reference exception if the rendered UIElement has no PresentationSource #1798

@albtaeler

Description

@albtaeler

Description

To export whole plot pages (like paper pages) as PNG we create a new ViewModel in our MVVM WPF application only for the export which is not displayed on the screen. We don't use the oxyplot pngexporter for this because the pages contains also textboxes and other elements.
After updating to the latest OxyPlot version and switching to SkiaSharp Render, this no longer works.
We get a NullreferenceException when exporting.

Steps to reproduce

Create and render a PlotView with code like this:

var mainGrid = new Grid() { Height = 400, Width = 600 };
mainGrid.Children.Add(new PlotView() { Model = plotModel });
var bmp = FrameworkElementToBitmap(mainGrid);
WritePngToFile(@"ExportTestSkiaRenderer.png", bmp);

Use a RenderTargetBitmap to render it as an image.

        private static BitmapSource FrameworkElementToBitmap(FrameworkElement frameworkElement)
        {

            var size = new Size(frameworkElement.Width , frameworkElement.Height );
            frameworkElement.Measure(size);
            frameworkElement.Arrange(new Rect(size));
            frameworkElement.UpdateLayout();
            var bmp = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96.0, 96.0, PixelFormats.Pbgra32);
            bmp.Render(frameworkElement);
            return bmp;
        }


        private void WritePngToFile(string fileName, BitmapSource bitmapImage)
        {
            using (var fileStream = File.Create(fileName))
            using (var pngStream = new MemoryStream())
            {
                var encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
                encoder.Save(pngStream);
                pngStream.WriteTo(fileStream);
            }
        }

This causes a NullReferenceException at:

var m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;            
scaleX = m.M11;
scaleY = m.M22;

and at

// get window to screen offset
var visualOffset = this.TransformToAncestor(GetAncestorWindowFromVisualTree(this)).Transform(default);

in OxySKElement.cs

Platform: x86 & x64
.NET version: 4.7.2
Latest OxyPlot Version (nuget packages build from develop brunch)

Expected behaviour

Rendering should be possible as in older versions.
Version f9cebf3 from 2019-01-31 didn't have this issue.

Actual behaviour

When exporting as described above we get a NullreferenceException.

Possible Fix

Return the default value if the PresentationSource or the ancestor visual is null:

            var compositionTarget = PresentationSource.FromVisual(this)?.CompositionTarget;
            if (compositionTarget != null)
            {
                var m = compositionTarget.TransformToDevice;
                scaleX = m.M11;
                scaleY = m.M22;
            }

and

            // get window to screen offset
            var ancestor = GetAncestorWindowFromVisualTree(this);
            var visualOffset = ancestor!=null ? this.TransformToAncestor(ancestor).Transform(default) : default;

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      X Tutup