context: #region Copyright & License Information
  /*
   * Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation, either version 3 of
   * the License, or (at your option) any later version. For more
   * information, see COPYING.
   */
  #endregion
  
  using System;
  using System.Globalization;
  using OpenRA.Scripting;
  
  namespace OpenRA.Primitives
  {
  	public readonly struct Color : IEquatable<Color>, IScriptBindable
  	{
  		readonly long argb;
  
  		public static Color FromArgb(int red, int green, int blue)
  		{
  			return FromArgb(byte.MaxValue, red, green, blue);
  		}
  
  		public static Color FromArgb(int alpha, int red, int green, int blue)
  		{
  			return new Color(((byte)alpha << 24) + ((byte)red << 16) + ((byte)green << 8) + (byte)blue);
  		}
  
  		public static Color FromAhsl(int alpha, float h, float s, float l)
  		{
  			// Convert HSL to HSV
  			var v = l + s * Math.Min(l, 1 - l);
  			var sv = v > 0 ? 2 * (1 - l / v) : 0;
  
  			return FromAhsv(alpha, h, sv, v);
  		}
  
  		public static Color FromAhsv(int alpha, float h, float s, float v)
  		{
  			var (r, g, b) = HsvToRgb(h, s, v);
  			return FromArgb(alpha, (byte)Math.Round(255 * r), (byte)Math.Round(255 * g), (byte)Math.Round(255 * b));
  		}
  
  		public static Color FromAhsv(float h, float s, float v)
  		{
  			return FromAhsv(255, h, s, v);
  		}
  
  		public (int A, float H, float S, float V) ToAhsv()
  		{
  			var (h, s, v) = RgbToHsv(R, G, B);
  			return (A, h, s, v);
  		}
  
  		Color(long argb)
  		{
  			this.argb = argb;
  		}
  
  		public int ToArgb()
  		{
  			return (int)argb;
  		}
  
  		public static Color FromArgb(int alpha, Color baseColor)
  		{
  			return FromArgb(alpha, baseColor.R, baseColor.G, baseColor.B);
  		}
  
  		public static Color FromArgb(int argb)
  		{
  			return FromArgb((byte)(argb >> 24), (byte)(argb >> 16), (byte)(argb >> 8), (byte)argb);
  		}
  
  		public static Color FromArgb(uint argb)
  		{
  			return FromArgb((byte)(argb >> 24), (byte)(argb >> 16), (byte)(argb >> 8), (byte)argb);
  		}
  
  		static float SrgbToLinear(float c)
  		{
  			// Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/
  			return c <= 0.04045f ? c / 12.92f : (float)Math.Pow((c + 0.055f) / 1.055f, 2.4f);
  		}
  
  		static float LinearToSrgb(float c)
  		{
  			// Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/
  			return c <= 0.0031308f ? c * 12.92f : 1.055f * (float)Math.Pow(c, 1.0f / 2.4f) - 0.055f;
  		}
  
  		public (float R, float G, float B) ToLinear()
  		{
  			// Undo pre-multiplied alpha and gamma correction
  			var r = SrgbToLinear((float)R / A);
  			var g = SrgbToLinear((float)G / A);
  			var b = SrgbToLinear((float)B / A);
  
  			return (r, g, b);
  		}
  
  		public static Color FromLinear(byte a, float r, float g, float b)
  		{
  			// Apply gamma correction and pre-multiplied alpha
  			return FromArgb(a,
  				(byte)Math.Round(LinearToSrgb(r) * a),
  				(byte)Math.Round(LinearToSrgb(g) * a),
  				(byte)Math.Round(LinearToSrgb(b) * a));
  		}
  
  		public static (float R, float G, float B) HsvToRgb(float h, float s, float v)
  		{
  			// Based on maths explained in http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
  			var px = Math.Abs(h * 6f - 3);
  			var py = Math.Abs((h + 2f / 3) % 1 * 6f - 3);
  			var pz = Math.Abs((h + 1f / 3) % 1 * 6f - 3);
  
  			var r = v * float2.Lerp(1f, (px - 1).Clamp(0, 1), s);
  			var g = v * float2.Lerp(1f, (py - 1).Clamp(0, 1), s);
  			var b = v * float2.Lerp(1f, (pz - 1).Clamp(0, 1), s);
  
  			return (r, g, b);
  		}
  
  		public static (float H, float S, float V) RgbToHsv(byte r, byte g, byte b)
  		{
  			return RgbToHsv(r / 255f, g / 255f, b / 255f);
  		}
  
  		public static (float H, float S, float V) RgbToHsv(float r, float g, float b)
  		{
  			// Based on maths explained in http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
  			var rgbMax = Math.Max(r, Math.Max(g, b));
  			var rgbMin = Math.Min(r, Math.Min(g, b));
  			var delta = rgbMax - rgbMin;
  			var v = rgbMax;
  
  			// Greyscale colors are defined to have hue and saturation 0
  			if (delta == 0.0f)
  				return (0, 0, v);
  
  			float hue;
  			if (r == rgbMax)
  				hue = (g - b) / (6 * delta);
  			else if (g == rgbMax)
  				hue = (b - r) / (6 * delta) + 1 / 3f;
  			else
  				hue = (r - g) / (6 * delta) + 2 / 3f;
  
  			var h = hue - (int)hue;
  
  			// Wrap negative values into [0-1)
  			if (h < 0)
  				h += 1;
  
  			var s = delta / rgbMax;
  			return (h, s, v);
  		}
  
  		public static bool TryParse(string value, out Color color)
  		{
  			color = default;
  			value = value.Trim();
  			if (value.Length != 6 && value.Length != 8)
  				return false;
  
  			byte alpha = 255;
  			if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var red)
  			    || !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var green)
  			    || !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var blue))
  				return false;
  
  			if (value.Length == 8
  			    && !byte.TryParse(value.Substring(6, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out alpha))
  				return false;
  
  			color = FromArgb(alpha, red, green, blue);
  			return true;
  		}
  
  		public static bool operator ==(Color left, Color right)
  		{
  			return left.argb == right.argb;
  		}
  
  		public static bool operator !=(Color left, Color right)
  		{
  			return !(left == right);
  		}
  
  		public float GetBrightness()
  		{
  			var min = Math.Min(R, Math.Min(G, B));
  			var max = Math.Max(R, Math.Max(G, B));
  			return (max + min) / 510f;
  		}
  
  		public byte A => (byte)(argb >> 24);
  		public byte R => (byte)(argb >> 16);
  		public byte G => (byte)(argb >> 8);
  		public byte B => (byte)argb;
  
  		public bool Equals(Color other)
  		{
  			return this == other;
  		}
  
  		public override bool Equals(object obj)
  		{
  			if (!(obj is Color))
  				return false;
  
  
gt: 			return this == (Color)obj;