using System; using System.Runtime.InteropServices; using Unity.Collections; using UnityEngine; namespace UnityEditor.Bindings.OpenImageIO { /// /// Class containing bindings for IO operations using OpenImageIO library /// public class OiioWrapper { #if UNITY_EDITOR_WIN const string LibraryPath = "OIIOBindings"; #elif UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX const string LibraryPath = "libOIIOBindings"; #else const string LibraryPath = "Undefined"; #endif /// /// Writes an image to the specified file name. /// /// Path of the file to write. /// Number of sub-images in this file. /// Pointer to ImageHeaders for the file to write. /// [DllImport(LibraryPath)] public static extern unsafe int WriteImage(FixedString4096Bytes filePath, uint nImages, ImageHeader* headers); /// /// Reads an image from its file name. /// /// Path of the file to read. /// A SubImagesList with the information of the file. [DllImport(LibraryPath)] public static extern unsafe SubImagesList ReadImage(FixedString4096Bytes filePath); /// /// Frees the memory allocated for the SubImagesList passed. /// /// SubImagesList representing information to clear. /// Zero (0) if successful. [DllImport(LibraryPath)] public static extern unsafe int FreeAllocatedMemory(SubImagesList list); /// /// A struct (key value pair) used to encode metadata in an image. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct Attribute { /// /// Attribute key (its unique name as expected by OIIO) /// public FixedString4096Bytes key; /// /// Attribute value /// public FixedString4096Bytes value; } /// /// A struct representing an Image Header /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct ImageHeader { /// /// Pointer to image data structure /// public IntPtr data; /// /// Number of channels in the image /// public uint channelsCount; /// /// Image width /// public uint width; /// /// Image height /// public uint height; /// /// Number of attributes /// public uint attributesCount; /// /// Pointer to attributes data structure /// public IntPtr attributes; } /// /// A struct representing the sub-images in an image. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct SubImagesList { /// /// Number of sub-images in this list. /// public uint subImagesCount; /// /// Pointer to sub-image data. /// public IntPtr data; } /// /// Reads an IntPtr into an Array of structs of length arrayCount. /// /// IntPtr pointing to the data to read. /// Number of items in the array. /// Type of struct for the array. /// An array of type T structs. public static T[] IntPtrToManagedArray(IntPtr pointerToArr, uint arrayCount) where T : struct { T[] managedArray = new T[arrayCount]; var sizeElement = Marshal.SizeOf(typeof(T)); for (int i = 0; i < arrayCount; i++) { IntPtr elemPtr = new IntPtr(pointerToArr.ToInt64() + i * sizeElement); managedArray[i] = Marshal.PtrToStructure(elemPtr); } return managedArray; } /// /// Reads an ImageHeader and returns it as a Texture2D. /// /// ImageHeader describing image to read. /// A Texture2D based on the passed ImageHeader. /// Throws if number of channels is not supported. public static Texture2D Tex2DFromImageHeader(ImageHeader subImgHeader) { int numChannels = (int)subImgHeader.channelsCount; int w = (int)subImgHeader.width; int h = (int)subImgHeader.height; float[] rawData = new float[w * h * numChannels]; unsafe { var sourcePtr = (ushort*)subImgHeader.data; for (int i = 0; i < 0 + w * h * numChannels; ++i) { rawData[i] = Mathf.HalfToFloat(*sourcePtr++); } } Color[] pixels = new Color[h * w]; for (int r = 0; r < h; r++) { for (int c = 0; c < w; c++) { int currPixel = r * w + c; int currOutPixel = (h - 1 - r) * w + c; switch (numChannels) { case 1: // only red pixels[currOutPixel] = new Color(rawData[currPixel * numChannels], 0, 0); break; case 2: // rg pixels[currOutPixel] = new Color(rawData[currPixel * numChannels], rawData[currPixel * numChannels + 1], 0); break; case 3: //rgb pixels[currOutPixel] = new Color(rawData[currPixel * numChannels], rawData[currPixel * numChannels + 1], rawData[currPixel * numChannels + 2]); break; case 4: //rgba pixels[currOutPixel] = new Color(rawData[currPixel * numChannels], rawData[currPixel * numChannels + 1], rawData[currPixel * numChannels + 2], rawData[currPixel * numChannels + 3]); break; default: throw new Exception( $"Unsupported number of channels - Should be 1, 2, 3 or 4 but was {numChannels}"); } } } var tex = new Texture2D(w, h, TextureFormat.RGBAHalf, false); tex.SetPixels(pixels); return tex; } } }