using System;
using System.Buffers;
using System.Reflection;
using UnityEngine;
using UnityEngine.Assertions;
using Object = UnityEngine.Object;
#if !UNITY_6000_0_OR_NEWER
using System.Linq.Expressions;
#endif
namespace RosettaUI.Builder
{
public static class GradientHelper
{
private delegate IntPtr GradientBindingsMarshallerConvertToNative(Gradient gradient);
private static GradientBindingsMarshallerConvertToNative _convertToNativeFunc;
private delegate bool GradientInternalEquals(Gradient gradient, IntPtr other);
private static GradientInternalEquals _internalEqualsFunc;
public static Gradient Clone(Gradient src)
{
var dst = new Gradient();
Copy(src, dst);
return dst;
}
public static void Copy(Gradient src, Gradient dst)
{
dst.SetKeys(src.colorKeys, src.alphaKeys);
dst.mode = src.mode;
dst.colorSpace = src.colorSpace;
}
///
/// Unity6以降ではアロケーション無しで値での比較
/// *たまにInternal_Equals 内でアロケーションするっぽい
/// 以下の操作はアロケーションが発生するので、Gradient内部の比較メソッドを利用する
/// - Gradient.colorKeys
/// - Gradient.Equals() : boxingが発生するため
///
public static bool EqualsWithoutAlloc(Gradient gradient, Gradient other)
{
if (ReferenceEquals(gradient, other)) return true;
if (gradient is null || other is null) return false;
if (_convertToNativeFunc == null)
{
#if UNITY_6000_0_OR_NEWER
var marshallerType = typeof(Gradient).GetNestedType("BindingsMarshaller", BindingFlags.NonPublic | BindingFlags.Static);
var method = marshallerType.GetMethod("ConvertToNative", BindingFlags.Public | BindingFlags.Static);
Assert.IsNotNull(method, "Gradient.BindingsMarshaller.ConvertToNative method not found.");
_convertToNativeFunc = (GradientBindingsMarshallerConvertToNative)Delegate.CreateDelegate(typeof(GradientBindingsMarshallerConvertToNative), method);
#else
// Unity6以前のバージョンではConvertToNativeメソッドがない
// FieldInfo.GetValue()はアロケーション(boxing)が発生するため、Expressionを使う
var targetParam = Expression.Parameter(typeof(Gradient), "target");
var field = typeof(Gradient).GetField("m_Ptr", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(field, "Gradient.m_Ptr field not found.");
var fieldAccess = Expression.Field(targetParam, field);
var lambda = Expression.Lambda(
fieldAccess,
targetParam
);
_convertToNativeFunc = lambda.Compile();
#endif
}
if (_internalEqualsFunc == null)
{
var method = typeof(Gradient).GetMethod("Internal_Equals", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.IsNotNull(method, "Gradient.Internal_Equals method not found.");
_internalEqualsFunc = (GradientInternalEquals)Delegate.CreateDelegate(typeof(GradientInternalEquals), method);
}
return _internalEqualsFunc(gradient, _convertToNativeFunc(other));
}
public static Texture2D GenerateOrUpdatePreviewTexture(Gradient gradient, Texture2D texture)
{
const int width = 256;
const int height = 1;
if (TextureUtility.EnsureTextureSize(ref texture, width, height))
{
texture.wrapMode = TextureWrapMode.Clamp;
texture.filterMode = FilterMode.Bilinear;
}
var colorArray = ArrayPool.Shared.Rent(width);
for (var i = 0; i < width; i++)
{
colorArray[i] = gradient.Evaluate(i / (float)width);
}
texture.SetPixels(0, 0, width, height, colorArray);
texture.Apply();
ArrayPool.Shared.Return(colorArray);
return texture;
}
}
}