// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Microsoft.MixedReality.GraphicsTools
{
///
/// Graphics Tools standard shader utility class with commonly used constants, types and convenience methods.
///
public static class StandardShaderUtility
{
///
/// The string name of the Standard shader which can be used to identify a shader or for shader lookups.
///
public static readonly string GraphicsToolsStandardShaderName = "Graphics Tools/Standard";
///
/// Returns an instance of the Graphics Tools/Standard shader.
///
public static Shader GraphicsToolsStandardShader
{
get
{
if (graphicsToolsStandardShader == null)
{
graphicsToolsStandardShader = Shader.Find(GraphicsToolsStandardShaderName);
}
return graphicsToolsStandardShader;
}
private set
{
graphicsToolsStandardShader = value;
}
}
private static Shader graphicsToolsStandardShader = null;
///
/// The string name of the Standard Canvas shader which can be used to identify a shader or for shader lookups.
///
public static readonly string GraphicsToolsStandardCanvasShaderName = "Graphics Tools/Standard Canvas";
///
/// Returns an instance of the Graphics Tools/Standard shader.
///
public static Shader GraphicsToolsStandardCanvasShader
{
get
{
if (graphicsToolsStandardCanvasShader == null)
{
graphicsToolsStandardCanvasShader = Shader.Find(GraphicsToolsStandardCanvasShaderName);
}
return graphicsToolsStandardCanvasShader;
}
private set
{
graphicsToolsStandardCanvasShader = value;
}
}
private static Shader graphicsToolsStandardCanvasShader = null;
///
/// Checks if a shader is the Graphics Tools/Standard or Standard Canvas shader.
///
/// The shader to check.
/// True if the shader is the shader.
public static bool IsGraphicsToolsStandardShader(Shader shader)
{
return shader == GraphicsToolsStandardShader ||
shader == GraphicsToolsStandardCanvasShader;
}
///
/// Checks if a material is using the Graphics Tools/Standard or Standard Canvas shader.
///
/// The material to check.
/// True if the material is using the shader
public static bool IsUsingGraphicsToolsStandardShader(Material material)
{
return IsGraphicsToolsStandardShader((material != null) ? material.shader : null);
}
///
/// The string name of the Graphics Tools Text Mesh Pro shader which can be used to identify a shader or for shader lookups.
///
public static readonly string GraphicsToolsTextMeshProShaderName = "Graphics Tools/Text Mesh Pro";
///
/// Returns an instance of the Graphics Tools/Text Mesh Pro shader.
///
public static Shader GraphicsToolsTextMeshProShader
{
get
{
if (graphicsToolsTextMeshProShader == null)
{
graphicsToolsTextMeshProShader = Shader.Find(GraphicsToolsTextMeshProShaderName);
}
return graphicsToolsTextMeshProShader;
}
private set
{
graphicsToolsTextMeshProShader = value;
}
}
private static Shader graphicsToolsTextMeshProShader = null;
///
/// Checks if a shader is the Graphics Tools/Text Mesh Pro shader.
///
/// The shader to check.
/// True if the shader is the shader.
public static bool IsGraphicsToolsTextMeshProShader(Shader shader)
{
return shader == GraphicsToolsTextMeshProShader;
}
///
/// Checks if a material is using the Graphics Tools/Text Mesh Pro shader.
///
/// The material to check.
/// True if the material is using the shader
public static bool IsUsingGraphicsToolsTextMeshProShader(Material material)
{
return IsGraphicsToolsTextMeshProShader((material != null) ? material.shader : null);
}
///
/// Shifts a source color in HSV color space.
///
public static Color ColorShiftHSV(Color source, float hueOffset, float saturationtOffset, float valueOffset)
{
float hue, saturation, value;
Color.RGBToHSV(source, out hue, out saturation, out value);
hue = hue + hueOffset;
saturation = Mathf.Clamp01(saturation + saturationtOffset);
value = Mathf.Clamp01(value + valueOffset);
Color output = Color.HSVToRGB(hue, saturation, value);
output.a = source.a;
return output;
}
///
/// Given a source color produces a visually pleasing" gradient by shifting the source color in HSV space.
///
public static void AutofillFourPointGradient(Color source, out Color topLeft, out Color topRight, out Color bottomLeft, out Color bottomRight, out Color stroke)
{
topLeft = source;
topRight = ColorShiftHSV(source, 0.02f, -0.2f, -0.1f);
bottomLeft = ColorShiftHSV(source, -0.03f, 0.1f, 0.3f);
bottomRight = ColorShiftHSV(source, 0.01f, -0.1f, 0.2f);
stroke = ColorShiftHSV(source, 0.01f, -0.2f, 0.0f);
}
///
/// Attempts to parse a CSS gradient (https://developer.mozilla.org/en-US/docs/Web/CSS/gradient) into an array of colors and times.
/// Note, only linear gradients are supported at the moment. And, not all CSS gradient features are supported.
/// An example input sting is: background: background: linear-gradient(90deg, #0380FD 0%, #406FC8 19.05%, #2B398F 49.48%, #FF77C1 100%);
///
public static bool TryParseCSSGradient(string cssGradient, out Color[] gradientColors, out float[] gradientTimes, out float gradientAngle)
{
const float defaultCSSAngle = 180.0f;
bool success;
try
{
// Extract the gradient structure.
const string prefix = "linear-gradient(";
const string postfix = ");";
int start = cssGradient.IndexOf(prefix) + prefix.Length;
int end = cssGradient.IndexOf(postfix, start);
string gradient = cssGradient.Substring(start, end - start);
string[] parameters = gradient.Split(',');
float angle = defaultCSSAngle;
List colorKeys = new List();
List timeKeys = new List();
// Parse each parameter.
for (int i = 0; i < parameters.Length; ++i)
{
// Handle degrees.
if (parameters[i].Contains("deg"))
{
float.TryParse(parameters[i].Replace("deg", string.Empty), out angle);
}
else // Handle colors and times.
{
// Parse rgba format.
if (parameters[i].Contains("rgba("))
{
float NormalizeColorChannel(float channel)
{
if (channel > 1.0f)
{
channel = channel / 255;
}
return channel;
}
float red, green, blue, alpha = 1.0f;
if (float.TryParse(parameters[i].Replace("rgba(", string.Empty), out red))
{
red = NormalizeColorChannel(red);
}
if (float.TryParse(parameters[i + 1], out green))
{
green = NormalizeColorChannel(green);
}
if (float.TryParse(parameters[i + 2], out blue))
{
blue = NormalizeColorChannel(blue);
}
string[] colorKey = parameters[i + 3].Split(new string[] { ") " }, StringSplitOptions.RemoveEmptyEntries);
if (float.TryParse(colorKey[0], out alpha))
{
alpha = NormalizeColorChannel(alpha);
}
colorKeys.Add(new Color(red, green, blue, alpha));
float time;
if (colorKey.Length > 1 &&
float.TryParse(colorKey[1].Replace("%", string.Empty), out time))
{
timeKeys.Add(time / 100);
}
else
{
timeKeys.Add(-1.0f); // Signal that we need to generate a time value.
}
i += 3;
}
else // Parse hex/name format.
{
string[] colors = parameters[i].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Color color;
if (ColorUtility.TryParseHtmlString(colors[0], out color))
{
colorKeys.Add(color);
float time;
if (colors.Length > 1 &&
float.TryParse(colors[1].Replace("%", string.Empty), out time))
{
timeKeys.Add(time / 100);
}
else
{
timeKeys.Add(-1.0f); // Signal that we need to generate a time value.
}
}
}
}
}
if (timeKeys.Count >= 2)
{
// If no times were provided, assume regular interval.
for (int i = 0; i < timeKeys.Count; ++i)
{
float time = timeKeys[i];
if (time < 0)
{
time = (timeKeys.Count != 1) ? (float)i / (timeKeys.Count - 1) : 0.0f;
timeKeys[i] = time;
}
}
// Ensure the last time goes to one.
timeKeys[colorKeys.Count - 1] = 1.0f;
success = true;
}
else
{
success = false;
}
gradientColors = colorKeys.ToArray();
gradientTimes = timeKeys.ToArray();
gradientAngle = angle;
}
catch
{
// Failed to parse gradient.
success = false;
gradientColors = null;
gradientTimes = null;
gradientAngle = defaultCSSAngle;
}
return success;
}
}
}