// Copyright (c) 2012, Yves Goergen, http://unclassified.software/source/colormath
//
// Copying and distribution of this file, with or without modification, are permitted provided the
// copyright notice and this notice are preserved. This file is offered as-is, without any warranty.
using System;
using System.Drawing;
namespace Unclassified.Drawing
{
public static class ColorMath
{
///
/// Blends two colors in the specified ratio.
///
/// First color.
/// Second color.
/// Ratio between both colors. 0 for first color, 1 for second color.
///
public static Color Blend(Color color1, Color color2, double ratio)
{
int a = (int) Math.Round(color1.A * (1 - ratio) + color2.A * ratio);
int r = (int) Math.Round(color1.R * (1 - ratio) + color2.R * ratio);
int g = (int) Math.Round(color1.G * (1 - ratio) + color2.G * ratio);
int b = (int) Math.Round(color1.B * (1 - ratio) + color2.B * ratio);
return Color.FromArgb(a, r, g, b);
}
public static Color Darken(Color color, double ratio)
{
return Blend(color, Color.Black, ratio);
}
public static Color Lighten(Color color, double ratio)
{
return Blend(color, Color.White, ratio);
}
public static HslColor RgbToHsl(Color rgb)
{
// Translated from JavaScript, part of coati
double h, s, l;
double r = (double) rgb.R / 255;
double g = (double) rgb.G / 255;
double b = (double) rgb.B / 255;
double min = Math.Min(Math.Min(r, g), b);
double max = Math.Max(Math.Max(r, g), b);
l = (max + min) / 2;
if (max == min)
h = 0;
else if (max == r)
h = (60 * (g - b) / (max - min)) % 360;
else if (max == g)
h = (60 * (b - r) / (max - min) + 120) % 360;
else //if (max == b)
h = (60 * (r - g) / (max - min) + 240) % 360;
if (h < 0)
h += 360;
if (max == min)
s = 0;
else if (l <= 0.5)
s = (max - min) / (2 * l);
else
s = (max - min) / (2 - 2 * l);
return new HslColor((byte) Math.Round((h / 360 * 256) % 256), (byte) Math.Round(s * 255), (byte) Math.Round(l * 255));
}
public static Color HslToRgb(HslColor hsl)
{
// Translated from JavaScript, part of coati
double h = (double) hsl.H / 256;
double s = (double) hsl.S / 255;
double l = (double) hsl.L / 255;
double q;
if (l < 0.5)
q = l * (1 + s);
else
q = l + s - l * s;
double p = 2 * l - q;
double[] t = new double[] {h + 1.0 / 3, h, h - 1.0 / 3};
byte[] rgb = new byte[3];
for (int i = 0; i < 3; i++)
{
if (t[i] < 0) t[i]++;
if (t[i] > 1) t[i]--;
if (t[i] < 1.0 / 6)
rgb[i] = (byte) Math.Round((p + ((q - p) * 6 * t[i])) * 255);
else if (t[i] < 1.0 / 2)
rgb[i] = (byte) Math.Round(q * 255);
else if (t[i] < 2.0 / 3)
rgb[i] = (byte) Math.Round((p + ((q - p) * 6 * (2.0 / 3 - t[i]))) * 255);
else
rgb[i] = (byte) Math.Round(p * 255);
}
return Color.FromArgb(rgb[0], rgb[1], rgb[2]);
}
///
/// Computes the real modulus value, not the division remainder.
/// This differs from the % operator only for negative numbers.
///
/// Dividend.
/// Divisor.
///
private static int Mod(int dividend, int divisor)
{
if (divisor <= 0) throw new ArgumentOutOfRangeException("divisor", "The divisor cannot be zero or negative.");
int i = dividend % divisor;
if (i < 0) i += divisor;
return i;
}
///
/// Computes the grey value value of a color.
///
///
///
public static byte ToGray(Color c)
{
return (byte) (c.R * 0.3 + c.G * 0.59 + c.B * 0.11);
}
///
/// Determines whether the color is dark or light.
///
///
///
public static bool IsDarkColor(Color c)
{
return ToGray(c) < 0x90;
}
}
public struct HslColor
{
private byte h, s, l;
public byte H { get { return h; } set { h = value; } }
public byte S { get { return s; } set { s = value; } }
public byte L { get { return l; } set { l = value; } }
public HslColor(byte h, byte s, byte l)
{
this.h = h;
this.s = s;
this.l = l;
}
}
}