using System; using Unity.Jobs; using Unity.Collections; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARCore { internal struct FlipVerticalJob : IJobParallelFor { public int width; public int height; [ReadOnly] [NativeDisableParallelForRestriction] public NativeSlice grayscaleIn; [WriteOnly] [NativeDisableParallelForRestriction] public NativeArray grayscaleOut; public void Execute(int row) { int inputOffset = (height - 1 - row) * width; int outputOffset = row * width; int lastOffset = outputOffset + width; while (outputOffset < lastOffset) { grayscaleOut[outputOffset++] = grayscaleIn[inputOffset++]; } } } internal struct ConvertRFloatToGrayscaleJob : IJobParallelFor { public int width; public int height; [ReadOnly] public NativeSlice rfloatIn; [WriteOnly] public NativeArray grayscaleImageOut; public void Execute(int row) { int inputOffset = (height - 1 - row) * width; int outputOffset = row * width; int lastOffset = outputOffset + width; while (outputOffset < lastOffset) { grayscaleImageOut[outputOffset++] = (byte)(rfloatIn[inputOffset++] * 255); } } } internal struct ConvertBGRA32ToGrayscaleJob : IJobParallelFor { public int width; public int height; [ReadOnly] [NativeDisableParallelForRestriction] public NativeSlice colorImageIn; [WriteOnly] [NativeDisableParallelForRestriction] public NativeArray grayscaleImageOut; public void Execute(int row) { int colorImageOffset = (height - 1 - row) * width * 4; int grayImageOffset = row * width; int lastOffset = grayImageOffset + width; while (grayImageOffset < lastOffset) { grayscaleImageOut[grayImageOffset++] = (byte)( colorImageIn[colorImageOffset ] * 0.11f + colorImageIn[colorImageOffset + 1] * 0.59f + colorImageIn[colorImageOffset + 2] * 0.3f); colorImageOffset += 4; } } } internal struct ConvertARGB32ToGrayscaleJob : IJobParallelFor { public int width; public int height; [ReadOnly] [NativeDisableParallelForRestriction] public NativeSlice colorImageIn; [WriteOnly] [NativeDisableParallelForRestriction] public NativeArray grayscaleImageOut; public void Execute(int row) { int colorImageOffset = (height - 1 - row) * width * 4; int grayImageOffset = row * width; int lastOffset = grayImageOffset + width; while (grayImageOffset < lastOffset) { //ARGB so need to account for ALPHA index. grayscaleImageOut[grayImageOffset++] = (byte)( colorImageIn[colorImageOffset + 1] * 0.3f + colorImageIn[colorImageOffset + 2] * 0.59f + colorImageIn[colorImageOffset + 3] * 0.11f); colorImageOffset += 4; } } } internal struct ConvertStridedToGrayscaleJob : IJobParallelFor { public int stride; public int width; public int height; // NB: NativeDisableParallelForRestriction to allow // us to read and write to indices other than the // one passed into the Execute method. This is because // we interpret the index as the row number and process // and entire row at a time. This takes about 75% the // time of doing it one pixel at a time. [ReadOnly] [NativeDisableParallelForRestriction] public NativeSlice colorImageIn; [WriteOnly] [NativeDisableParallelForRestriction] public NativeArray grayscaleImageOut; public void Execute(int row) { int colorImageOffset = (height - 1 - row) * width * stride; int grayImageOffset = row * width; int lastOffset = grayImageOffset + width; while (grayImageOffset < lastOffset) { grayscaleImageOut[grayImageOffset++] = (byte)( colorImageIn[colorImageOffset ] * 0.3f + colorImageIn[colorImageOffset + 1] * 0.59f + colorImageIn[colorImageOffset + 2] * 0.11f); colorImageOffset += stride; } } } internal static class ConversionJob { public static JobHandle Schedule( NativeSlice inputImage, Vector2Int sizeInPixels, TextureFormat format, NativeArray grayscaleImage, JobHandle inputDeps) { int width = sizeInPixels.x; int height = sizeInPixels.y; if ((format == TextureFormat.R8) || (format == TextureFormat.Alpha8)) { return new FlipVerticalJob { width = width, height = height, grayscaleIn = inputImage, grayscaleOut = grayscaleImage }.Schedule(height, 1, inputDeps); } // We'll have to convert it. Create an output buffer. if (format == TextureFormat.RGB24) { return new ConvertStridedToGrayscaleJob { stride = 3, width = width, height = height, colorImageIn = inputImage, grayscaleImageOut = grayscaleImage }.Schedule(height, 1, inputDeps); } else if (format == TextureFormat.RGBA32) { return new ConvertStridedToGrayscaleJob { stride = 4, width = width, height = height, colorImageIn = inputImage, grayscaleImageOut = grayscaleImage }.Schedule(height, 1, inputDeps); } else if (format == TextureFormat.ARGB32) { return new ConvertARGB32ToGrayscaleJob { width = width, height = height, colorImageIn = inputImage, grayscaleImageOut = grayscaleImage }.Schedule(height, 1, inputDeps); } else if (format == TextureFormat.BGRA32) { return new ConvertBGRA32ToGrayscaleJob { width = width, height = height, colorImageIn = inputImage, grayscaleImageOut = grayscaleImage }.Schedule(height, 1, inputDeps); } else if (format == TextureFormat.RFloat) { return new ConvertRFloatToGrayscaleJob { width = width, height = height, rfloatIn = inputImage.SliceConvert(), grayscaleImageOut = grayscaleImage }.Schedule(height, 1, inputDeps); } else { throw new InvalidOperationException($"Texture format {format} is not supported."); } } } }