namespace MagicLeap.Android { using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Jobs; using UnityEngine; using UnityEngine.XR.MagicLeap.Unsafe; public static class YUVUtility { private const float kRedCoefficient = 1.370705f; private const float kGreenCoefficientV = 0.698001f; private const float kGreenCoefficientU = 0.337633f; private const float kBlueCoefficient = 1.732446f; private struct YuvToRgbJob : IJobFor { public NativePlane Y; public NativePlane U; public NativePlane V; public ImageDimensions Dimensions; public NativeArray RgbData; public void Execute(int index) { (int x, int y) = Dimensions.GetCoordinatesFromIndex(index); int yIndex = CalculateYIndex(x, y, Y.PixelStride, Y.RowStride); int uvIndex = CalculateUVIndex(x, y, U.PixelStride, U.RowStride); byte rawY = Y.GetDataAtOffset(yIndex); byte rawU = U.GetDataAtOffset(uvIndex); byte rawV = V.GetDataAtOffset(uvIndex); RgbData[index] = ProcessPixel(rawY, rawU, rawV); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CalculateYIndex(int x, int y, int pixelStride, int rowStride) => (y * rowStride) + (x * pixelStride); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CalculateUVIndex(int x, int y, int pixelStride, int rowStride) { int uvx = x / 2; int uvy = y / 2; return (uvy * rowStride) + (uvx * pixelStride); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ClampToByte(int value) => (byte)Mathf.Clamp(value, 0, 255); public static JobHandle ConvertPlanesToRGBAsync(Allocator allocator, NativeYUVPlanes yuvPlanes, out NativeArray outRgb) { outRgb = yuvPlanes.Dimensions.CreateNativeArray(allocator); var job = new YuvToRgbJob { Y = yuvPlanes.YPlane, U = yuvPlanes.UPlane, V = yuvPlanes.VPlane, Dimensions = yuvPlanes.Dimensions, RgbData = outRgb, }; return job.ScheduleParallel(yuvPlanes.Dimensions.Size, yuvPlanes.Dimensions.Width, default); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color32 ProcessPixel(byte rawY, byte rawU, byte rawV) { int y = rawY & 0xFF; int u = (rawU & 0xFF) - 128; int v = (rawV & 0xFF) - 128; int r = (int)(y + kRedCoefficient * v); int g = (int)(y - (kGreenCoefficientV * v) - (kGreenCoefficientU * u)); int b = (int)(y + kBlueCoefficient * u); return new Color32 { a = 255, r = ClampToByte(r), g = ClampToByte(g), b = ClampToByte(b) }; } } }