Skip to content

Instantly share code, notes, and snippets.

@copernicus365
Last active September 3, 2019 04:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save copernicus365/d21116ca4eb1705f8eaa to your computer and use it in GitHub Desktop.
Save copernicus365/d21116ca4eb1705f8eaa to your computer and use it in GitHub Desktop.
ColorRGB - C# hex color parser that gets the R,G,B values of the hex color as well as it's Luminance, YIC, and IsDark values.
using System;
using System.Collections.Generic;
using System.Globalization;
namespace DotNetXtensions
{
public struct ColorRGB
{
public byte R { get; private set; }
public byte G { get; private set; }
public byte B { get; private set; }
public string HexColor { get; private set; }
public int DecimalHexColor { get; private set; }
// --- Calculated Values ---
/// <summary>
/// http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
/// Formula calculated here thanks to Brian Suda: http://24ways.org/2010/calculating-color-contrast/
/// </summary>
public byte Luminance { get; private set; }
/// <summary>
/// https://en.wikipedia.org/wiki/YIQ
/// </summary>
public byte YIC { get; private set; }
public bool IsDark { get; private set; }
/// <summary>
/// (Source: I (Nicholas Petersen) converted this over from a hodge podge of
/// JavaScript code I had gotten from other sources and was using for some years.
/// This article was influential: https://24ways.org/2010/calculating-color-contrast/)
/// </summary>
/// <param name="hex"></param>
public ColorRGB(string hex)
{
HexColor = hex = FixAndBasicValidateHexColorString(hex);
// get the colors from each two digits, first way only has to parse hex string once so is pry more performant
DecimalHexColor = Int32.Parse(hex, NumberStyles.HexNumber);
if(hex.ToUpper() != DecimalHexColor.ToString("X"))
throw new ArgumentException();
R = (byte)((DecimalHexColor >> 16) & byte.MaxValue /*255*/);
G = (byte)((DecimalHexColor >> 8) & byte.MaxValue);
B = (byte)(DecimalHexColor & byte.MaxValue);
// or simply substring every two digits
//R = byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
//G = byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
//B = byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
// NOTE: in my calculation the highest either Luminance or YIC can be is 255, so I'm keeping them a byte
Luminance = (byte)(0.2126 * R + 0.7152 * G + 0.0722 * B); // per ITU-R BT.709;
YIC = (byte)(((R * 299) + (G * 587) + (B * 114)) / 1000);
// uses both luma and yic, gives much better results
//IsDark = YIC < 129 || Luminance < 40;
//IsDark = YIC < 193 || Luminance < 80;
//IsDark = YIC < 161 || Luminance < 64;
//IsDark = YIC < 169 || Luminance < 72;
IsDark = YIC < 177 || Luminance < 80;
}
public static string FixAndBasicValidateHexColorString(string hex)
{
if(hex.IsNulle()) throw new ArgumentNullException();
if(hex[0] == '#') hex = hex.Substring(1); // remove initial '#' if present
if(hex.Length == 3)
hex = hex + hex; // this is only valid, I think, if all chars are the same char, but not going to check
if(hex.Length != 6) throw new ArgumentOutOfRangeException();
return hex;
}
public override string ToString()
{
return $"#{HexColor} [{DecimalHexColor + "]",-20} R:{R,-5} G:{G,-5} B:{B,-5} Dark:{(IsDark ? "T" : "F"),-5} Lum:{Luminance,-5} YIC:{YIC,-5}";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment