Search code examples
c#.netimagesharp

Unexpected behaviour when pinning object in c#


I was playing with ImageSharp lib for C#, when I executed this code

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace ImageSharp
{
    public class Program
    {
        public static void Main()
        {
            Image<Rgba32> img = null;
            using (var imageFileStream = new FileStream(/*Any jpg image.*/@"E:\cat\0.jpg", FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                img = Image.Load(imageFileStream);
            }
            int length = img.Height * img.Width / 2;
            //Rgba32[] colors = typeof(Rgba32).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).Where(a => a.FieldType == typeof(Rgba32)).Select(a => (Rgba32)a.GetValue(null)).ToArray();
            Span<Rgba32> buffer = Span<Rgba32>.Empty;
            GCHandle bufferHandle = GCHandle.Alloc(img.DangerousGetPinnableReferenceToPixelBuffer(), GCHandleType.Pinned);
            unsafe
            {
                buffer = new Span<Rgba32>(bufferHandle.AddrOfPinnedObject().ToPointer(), length);
            }
            for (int i = 0; i < length; i++)
            {
                buffer[i] = Rgba32.Yellow;
                Console.WriteLine(i);//exception thrown here
            }
            buffer = Span<Rgba32>.Empty;
            bufferHandle.Free();
            GC.Collect();
            using (var imageFileStream = new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), @"ImageSharp.jpg"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read))
            {
                img.SaveAsJpeg(imageFileStream);
                imageFileStream.Flush();
            }
            Console.WriteLine("Done!");
            Console.ReadLine();
        }
    }
}

I got this exception (System.IO.IOException: 'The handle is invalid.'). And If you remove the line where the exception is thrown the program will just hang (I think it will hang inside the loop).

So my question is what caused this exception and why when you remove "Console.WriteLine" the program will hang ?

The only dependency in the project is ImageSharp nuget package. Framework version : 4.7.1


Solution

  • It looks like you are trying to pin the buffer, and walk it using Span<T>

    You mistake is here.

    GCHandle.Alloc(img.DangerousGetPinnableReferenceToPixelBuffer(), GCHandleType.Pinned);
    

    You're boxing a struct representing the first pixel of the image, and creating a GCHandle to that boxed object instance.

    If you really need to pin the buffer I would suggest doing this instead.

    fixed (Rgba32* ptr = &img.DangerousGetPinnableReferenceToPixelBuffer())
    {
        Console.WriteLine(ptr[42]); // no Span<T> needed!
    }
    

    However... I don't really understand why you are trying to do this. There is per-pixel access already available via the API using the Image<TPixel>[x,y] indexer and methods available to Fill pixel areas. I strongly suggest you use the methods available.