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;
}
}
}