Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullReferenceException in Avalonia.Skia when creating a new Bitmap #16067

Open
SKeehnen opened this issue Jun 19, 2024 · 2 comments
Open

NullReferenceException in Avalonia.Skia when creating a new Bitmap #16067

SKeehnen opened this issue Jun 19, 2024 · 2 comments
Labels

Comments

@SKeehnen
Copy link

Describe the bug

With Avalonia 11.1.0-beta2 and 11.1.0-rc1 I get the following error when creating a new Bitmap:

Exception thrown: 'System.NullReferenceException' in Avalonia.Skia.dll: 'Object reference not set to an instance of an object.'
Stack trace:
 >   at Avalonia.Skia.ImmutableBitmap..ctor(PixelSize size, Vector dpi, Int32 stride, PixelFormat format, AlphaFormat alphaFormat, IntPtr data)

This worked without problems in 11.1.0-beta1 and earlier.

To Reproduce

I'm using the following callback function to create an image from a buffer:

public void AfterFrameDraw(nint buffer_data, int frame_num, int width, int height, int bufferWidth, int bufferHeight)
{
    try
    {
        if (isDisposing || width == 0 || height == 0 || bufferWidth == 0 || bufferHeight == 0)
            return;

        if (width == bufferWidth && height == bufferHeight && (width != this.Bounds.Width || height != this.Bounds.Height) && this.Bounds.Width > 0 && this.Bounds.Height > 0)
        {
            width = (int) this.Bounds.Width;
            height = (int) this.Bounds.Height;
        }

        // Mapviewer buffer is a 32-bit RGBA image
        int bufferChannels = 4; // RGBA

        // Get byte array from unmanaged data pointer
        byte[] byteArray = new byte[bufferHeight * bufferWidth * bufferChannels];
        Marshal.Copy(buffer_data, byteArray, 0, bufferHeight * bufferWidth * bufferChannels);

        int bytesPerPixel = 4; // For a 32-bit RGBA image
        byte[] data = new byte[height * width * bytesPerPixel];

        // Calculate the effective width and height to be copied
        int cropWidth = Math.Min(width, bufferWidth);
        int cropHeight = Math.Min(height, bufferHeight);

        Parallel.For(0, cropHeight, k =>
        {
            int srcOffset = ((cropHeight - k - 1) * bufferWidth) * bytesPerPixel;
            int destOffset = k * cropWidth * bytesPerPixel;
            Buffer.BlockCopy(
                byteArray, srcOffset,
                data, destOffset,
                cropWidth * bytesPerPixel);
        });

        Avalonia.Media.Imaging.Bitmap? bmImage = null;

        unsafe
        {
            fixed (byte* p = data)
            {
                IntPtr ptr = (IntPtr) p;

                bmImage = new Avalonia.Media.Imaging.Bitmap(Avalonia.Platform.PixelFormat.Rgba8888, Avalonia.Platform.AlphaFormat.Premul,
                ptr,
                new Avalonia.PixelSize(cropWidth, cropHeight),
                new Avalonia.Vector(96, 96),
                bufferChannels);
            }
        }

        MainThreadHelper.BeginInvokeOnMainThread(() =>
        {
            this.Source = bmImage;
        });

    }
    catch (Exception ex)
    {
        // Handle the exception (logging, etc.)
    }
}

The exception happens at

bmImage = new Avalonia.Media.Imaging.Bitmap(Avalonia.Platform.PixelFormat.Rgba8888, Avalonia.Platform.AlphaFormat.Premul,
                ptr,
                new Avalonia.PixelSize(cropWidth, cropHeight),
                new Avalonia.Vector(96, 96),
                bufferChannels);

Expected behavior

This should just work like it did in 11.1.0-beta1 and earlier.

Avalonia version

11.1.0-beta2, 11.1.0-rc1

OS

Linux

Additional context

No response

@SKeehnen SKeehnen added the bug label Jun 19, 2024
@MrJul
Copy link
Member

MrJul commented Jun 19, 2024

The stride parameter you're providing here (the last one in Bitmap's constructor) is incorrect.
It should probably be cropWidth * bufferChannels.

Before #15520, this parameter was incorrectly ignored so it worked for the wrong reason :)

That being said, we shouldn't throw a NullReferenceException here. Instead, Avalonia should check whether the internal InstallPixels call failed, and throw an ArgumentException for invalid data instead.

@SKeehnen
Copy link
Author

Yes that works, thanks @MrJul! Indeed the NullReferenceException had me fooled there. ArgumentException would be the right one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants