Search code examples
unity-game-enginetexture2d

Unity: Difference between GetPixels32() & GetPixels32(colors)


I am making a mobile application using Unity where I open the camera and get data from it using WebcamTexture.

The data which I get from the camera is Color32[].

I stored the data in the memory and then read it from memory:

// Address the variable (data) to a memory location
data = new Color32[width * height];
gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);

// this didn't work
data = webcamTexture.GetPixels32();

// this worked fine
webcamTexture.GetPixels32(data);

// read data from memory using this
gcHandle.AddrOfPinnedObject() 

When I used webcamTexture.GetPixels32(data); I was able to see the image of the camera, while when I used data = webcamTexture.GetPixels32(); I am getting a black screen from the camera.

My Questions:

1. Is there a difference between the two functions?

2. What I should do if I want to get Color32[] from Texture2D? (Texture2D has 2 functions to get Color32[]: GetPixels32() and GetRawTextureData<Color32>().ToArray(), both giving me a black screen)


Solution

  • As to the title

    Difference between GetPixels32() & GetPixels32(colors)

    If you look into the API you will see that the method signature is

    public Color32[] GetPixels32(Color32[] colors = null);
    

    so colors is an optional parameter.

    So on a very high level perspective: There is no difference, both are the same method.


    However, this parameter wouldn't exist, if the method wouldn't behave different with or without it.

    If you read on

    You can optionally pass in an array of Color32s to use in colors to avoid allocating new memory each frame, which is faster when you are continuously reading data from the camera. The array needs to be initialized to a length matching width * height of the texture. If you don't pass an array, GetPixels32 will allocate one for you and return it.

    you see that the difference in passing in an array or not is that if you don't do it, it allocates a new array for every time you call it. This is of course a lot more expensive if you are using this every frame. But letting aside this performance impact it also means that this way you get a new array reference every time.


    So this said now looking again into your code you are doing

    data = new Color32[width * height];
    gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
    

    so if you are filling this array it is still the same array object

    webcamTexture.GetPixels32(data);
    var ptr = gcHandle.AddrOfPinnedObject();
    

    so this works since you are filling the same array reference that is pinned in the gcHandle.


    On the other side this does not work

    data = webcamTexture.GetPixels32();
    var ptr = gcHandle.AddrOfPinnedObject();
    

    since here you create a new array reference. On this one you didn't call

    gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
    

    on and thus, when you reach

    gcHandle.AddrOfPinnedObject()
    

    this gcHandle still references to the previous Color32[] which was referenced by data the moment you did the Alloc call. That is still an empty array you did never write to.


    For Texture2D it should be GetPixels32() but you would probably want to first get that array before getting the pointer to it:

    var data = texture2D.GetPixels32();
    var gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
    var ptr = gcHandle.AddrOfPinnedObject();