9

I am working with Unity 4.5, grabbing images as bytes arrays (each byte represent a channel, taking 4 bytes per pixel (rgba) and displaying them on a texture converting the array to a Color32 array, using this loop:

   img = new Color32[byteArray.Length / nChannels]; //nChannels being 4
   for (int i=0; i< img.Length; i++) {
        img[i].r = byteArray[i*nChannels];
        img[i].g = byteArray[i*nChannels+1];
        img[i].b = byteArray[i*nChannels+2];
        img[i].a = byteArray[i*nChannels+3];
    }

Then, it is applied to the texture using:

tex.SetPixels32(img);

However, this slows down the application significantly (this loop is executed on every single frame), and I would like to know if there is any other way to speed up the copying process. I've found some people (Fast copy of Color32[] array to byte[] array) using the Marshal.Copy functions in order to do the reverse process (Color32 to byte array), but I have not been able to make it work to copy a byte array to a Color32 array. Does anybody know a faster way?

Thank you in advance!

1
  • 2
    Well you could just make your i++, i+=nChannels.... Commented Aug 14, 2014 at 15:18

5 Answers 5

14

Yes, Marshal.Copy is the way to go. I've answered a similar question here.

Here's a generic method to copy from struct[] to byte[] and vice versa

private static byte[] ToByteArray<T>(T[] source) where T : struct
{
    GCHandle handle = GCHandle.Alloc(source, GCHandleType.Pinned);
    try
    {
        IntPtr pointer = handle.AddrOfPinnedObject();
        byte[] destination = new byte[source.Length * Marshal.SizeOf(typeof(T))];
        Marshal.Copy(pointer, destination, 0, destination.Length);
        return destination;
    }
    finally
    {
        if (handle.IsAllocated)
            handle.Free();
    }
}

private static T[] FromByteArray<T>(byte[] source) where T : struct
{
    T[] destination = new T[source.Length / Marshal.SizeOf(typeof(T))];
    GCHandle handle = GCHandle.Alloc(destination, GCHandleType.Pinned);
    try
    {
        IntPtr pointer = handle.AddrOfPinnedObject();
        Marshal.Copy(source, 0, pointer, source.Length);
        return destination;
    }
    finally
    {
        if (handle.IsAllocated)
            handle.Free();
    }
}

Use it as:

[StructLayout(LayoutKind.Sequential)]
public struct Demo
{
    public double X;
    public double Y;
}

private static void Main()
{
    Demo[] array = new Demo[2];
    array[0] = new Demo { X = 5.6, Y = 6.6 };
    array[1] = new Demo { X = 7.6, Y = 8.6 };

    byte[] bytes = ToByteArray(array);
    Demo[] array2 = FromByteArray<Demo>(bytes);
}
Sign up to request clarification or add additional context in comments.

Comments

8

This code requires unsafe switch but should be fast. I think you should benchmark these answers...

var bytes = new byte[] { 1, 2, 3, 4 };

var colors = MemCopyUtils.ByteArrayToColor32Array(bytes);

public class MemCopyUtils
{
    unsafe delegate void MemCpyDelegate(byte* dst, byte* src, int len);
    static MemCpyDelegate MemCpy;

    static MemCopyUtils()
    {
        InitMemCpy();
    }

    static void InitMemCpy()
    {
        var mi = typeof(Buffer).GetMethod(
            name: "Memcpy",
            bindingAttr: BindingFlags.NonPublic | BindingFlags.Static,
            binder:  null,
            types: new Type[] { typeof(byte*), typeof(byte*), typeof(int) },
            modifiers: null);
        MemCpy = (MemCpyDelegate)Delegate.CreateDelegate(typeof(MemCpyDelegate), mi);
    }

    public unsafe static Color32[] ByteArrayToColor32Array(byte[] bytes)
    {
        Color32[] colors = new Color32[bytes.Length / sizeof(Color32)];

        fixed (void* tempC = &colors[0])
        fixed (byte* pBytes = bytes)
        {
            byte* pColors = (byte*)tempC;
            MemCpy(pColors, pBytes, bytes.Length);
        }
        return colors;
    }
}

3 Comments

I really like this - is there any overhead associated with calling Memcpy in this context?
@ChrisMantle I don't think, since the delegate is created only once
Small word of caution for Unity3D devs (tagged in OPs question), IIRC unsafe isn't supported in Unity3D or at least not for all platforms.
1

Using Parallel.For may give you a significant performance increase.

img = new Color32[byteArray.Length / nChannels]; //nChannels being 4
Parallel.For(0, img.Length, i =>
{
    img[i].r = byteArray[i*nChannels];
    img[i].g = byteArray[i*nChannels+1];
    img[i].b = byteArray[i*nChannels+2];
    img[i].a = byteArray[i*nChannels+3];
});

Example on MSDN

1 Comment

Sadly, the Mono framework does not include the not contain the 'System.Threading.Tasks' assembly. However, parallelizing the loop seems a nice idea. I'll look for a workaround!
1

I haven't profiled it, but using fixed to ensure your memory doesn't get moved around and to remove bounds checks on array accesses might provide some benefit:

img = new Color32[byteArray.Length / nChannels]; //nChannels being 4
fixed (byte* ba = byteArray)
{
    fixed (Color32* c = img)
    {
        byte* byteArrayPtr = ba;
        Color32* colorPtr = c;
        for (int i = 0; i < img.Length; i++)
        {
            (*colorPtr).r = *byteArrayPtr++;
            (*colorPtr).g = *byteArrayPtr++;
            (*colorPtr).b = *byteArrayPtr++;
            (*colorPtr).a = *byteArrayPtr++;
            colorPtr++;
        }
    }
}

It might not provide much more benefit on 64-bit systems - I believe that the bounds checking is is more highly optimized. Also, this is an unsafe operation, so take care.

Comments

1
public Color32[] GetColorArray(byte[] myByte)
{
    if (myByte.Length % 1 != 0) 
       throw new Exception("Must have an even length");

    var colors = new Color32[myByte.Length / nChannels];

    for (var i = 0; i < myByte.Length; i += nChannels)
    {
       colors[i / nChannels] = new Color32(
           (byte)(myByte[i] & 0xF8),
           (byte)(((myByte[i] & 7) << 5) | ((myByte[i + 1] & 0xE0) >> 3)),
           (byte)((myByte[i + 1] & 0x1F) << 3),
           (byte)1);
    }

    return colors;
}

Worked about 30-50 times faster than just i++. The "extras" is just styling. This code is doing, in one "line", in the for loop, what you're declaring in 4 lines plus it is much quicker. Cheers :)

Referenced + Referenced code: Here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.