unit ascolors;
{$WARN SYMBOL_PLATFORM OFF}
{$WARN DUPLICATE_CTOR_DTOR OFF}
interface
uses
Windows, SysUtils, Types, Math,
{$IF CompilerVersion >= 22}Vcl.Graphics{$ELSE}Graphics{$IFEND},
Generics.Defaults, Generics.Collections;
type
EColorException = class(Exception);
PRGB = ^TRGB;
TRGB = packed record
Red, Green, Blue: Double;
constructor Create(const ARed, AGreen, ABlue: Double); overload;
class operator Implicit(const APascalColor: TColor): TRGB;
class operator Implicit(const AColor: TRGB): TColor; inline;
class operator Equal(const AColor1, AColor2: TRGB): Boolean;
class operator NotEqual(const AColor1, AColor2: TRGB): Boolean; inline;
class operator Add(const AColor1, AColor2: TRGB): TRGB; inline;
class operator Subtract(const AColor1, AColor2: TRGB): TRGB; inline;
class operator Multiply(const AFactor: Double; const AColor: TRGB): TRGB; inline;
class operator Divide(const AColor: TRGB; const ADenominator: Double): TRGB; inline;
function IsValid: Boolean;
function Invert: TRGB;
function Average: Double;
end;
PHSL = ^THSL;
THSL = packed record
Hue, Saturation, Lightness: Double;
constructor Create(const AHue, ASaturation, ALightness: Double); overload;
class operator Implicit(const APascalColor: TColor): THSL; inline;
class operator Implicit(const AColor: THSL): TColor; inline;
class operator Explicit(const AColor: THSL): TRGB;
class operator Explicit(const AColor: TRGB): THSL;
class operator Equal(const AColor1, AColor2: THSL): Boolean;
class operator NotEqual(const AColor1, AColor2: THSL): Boolean; inline;
function IsValid: Boolean;
end;
PHSV = ^THSV;
THSV = packed record
Hue, Saturation, Value: Double;
constructor Create(const AHue, ASaturation, AValue: Double); overload;
class operator Implicit(const APascalColor: TColor): THSV; inline;
class operator Implicit(const AColor: THSV): TColor; inline;
class operator Explicit(const AColor: THSV): TRGB;
class operator Explicit(const AColor: TRGB): THSV;
class operator Equal(const AColor1, AColor2: THSV): Boolean;
class operator NotEqual(const AColor1, AColor2: THSV): Boolean; inline;
function IsValid: Boolean;
end;
PColorComponents = ^TColorComponents;
TColorComponents = record
Components: array[0..2] of Double;
function Min: Double; inline;
function Max: Double; inline;
class operator Equal(const AColor1, AColor2: TColorComponents): Boolean; inline;
class operator NotEqual(const AColor1, AColor2: TColorComponents): Boolean; inline;
end;
const
MIN_COLOR_COMPONENT = 0;
MAX_COLOR_COMPONENT = 2;
function SameColor(AColor1, AColor2: TColor): Boolean; inline;
function RandomColor: TColor; inline;
function CompareColor(const A, B: THSV): TValueRelationship; overload;
function CompareColor(const A, B: TRGB): TValueRelationship; overload;
type
TNamedColor = record
Name: string;
Value: TColor;
end;
const
NamedColors: array[0..146] of TNamedColor =
(
(Name: 'aliceblue'; Value: $00FFF8F0),
(Name: 'antiquewhite'; Value: $00D7EBFA),
(Name: 'aqua'; Value: $00FFFF00),
(Name: 'aquamarine'; Value: $00D4FF7F),
(Name: 'azure'; Value: $00FFFFF0),
(Name: 'beige'; Value: $00DCF5F5),
(Name: 'bisque'; Value: $00C4E4FF),
(Name: 'black'; Value: $00000000),
(Name: 'blanchedalmond'; Value: $00CDEBFF),
(Name: 'blue'; Value: $00FF0000),
(Name: 'blueviolet'; Value: $00E22B8A),
(Name: 'brown'; Value: $002A2AA5),
(Name: 'burlywood'; Value: $0087B8DE),
(Name: 'cadetblue'; Value: $00A09E5F),
(Name: 'chartreuse'; Value: $0000FF7F),
(Name: 'chocolate'; Value: $001E69D2),
(Name: 'coral'; Value: $00507FFF),
(Name: 'cornflowerblue'; Value: $00ED9564),
(Name: 'cornsilk'; Value: $00DCF8FF),
(Name: 'crimson'; Value: $003C14DC),
(Name: 'cyan'; Value: $00FFFF00),
(Name: 'darkblue'; Value: $008B0000),
(Name: 'darkcyan'; Value: $008B8B00),
(Name: 'darkgoldenrod'; Value: $000B86B8),
(Name: 'darkgray'; Value: $00A9A9A9),
(Name: 'darkgreen'; Value: $00006400),
(Name: 'darkgrey'; Value: $00A9A9A9),
(Name: 'darkkhaki'; Value: $006BB7BD),
(Name: 'darkmagenta'; Value: $008B008B),
(Name: 'darkolivegreen'; Value: $002F6B55),
(Name: 'darkorange'; Value: $00008CFF),
(Name: 'darkorchid'; Value: $00CC3299),
(Name: 'darkred'; Value: $0000008B),
(Name: 'darksalmon'; Value: $007A96E9),
(Name: 'darkseagreen'; Value: $008FBC8F),
(Name: 'darkslateblue'; Value: $008B3D48),
(Name: 'darkslategray'; Value: $004F4F2F),
(Name: 'darkslategrey'; Value: $004F4F2F),
(Name: 'darkturquoise'; Value: $00D1CE00),
(Name: 'darkviolet'; Value: $00D30094),
(Name: 'deeppink'; Value: $009314FF),
(Name: 'deepskyblue'; Value: $00FFBF00),
(Name: 'dimgray'; Value: $00696969),
(Name: 'dimgrey'; Value: $00696969),
(Name: 'dodgerblue'; Value: $00FF901E),
(Name: 'firebrick'; Value: $002222B2),
(Name: 'floralwhite'; Value: $00F0FAFF),
(Name: 'forestgreen'; Value: $00228B22),
(Name: 'fuchsia'; Value: $00FF00FF),
(Name: 'gainsboro'; Value: $00DCDCDC),
(Name: 'ghostwhite'; Value: $00FFF8F8),
(Name: 'gold'; Value: $0000D7FF),
(Name: 'goldenrod'; Value: $0020A5DA),
(Name: 'gray'; Value: $00808080),
(Name: 'green'; Value: $00008000),
(Name: 'greenyellow'; Value: $002FFFAD),
(Name: 'grey'; Value: $00808080),
(Name: 'honeydew'; Value: $00F0FFF0),
(Name: 'hotpink'; Value: $00B469FF),
(Name: 'indianred'; Value: $005C5CCD),
(Name: 'indigo'; Value: $0082004B),
(Name: 'ivory'; Value: $00F0FFFF),
(Name: 'khaki'; Value: $008CE6F0),
(Name: 'lavender'; Value: $00FAE6E6),
(Name: 'lavenderblush'; Value: $00F5F0FF),
(Name: 'lawngreen'; Value: $0000FC7C),
(Name: 'lemonchiffon'; Value: $00CDFAFF),
(Name: 'lightblue'; Value: $00E6D8AD),
(Name: 'lightcoral'; Value: $008080F0),
(Name: 'lightcyan'; Value: $00FFFFE0),
(Name: 'lightgoldenrodyellow'; Value: $00D2FAFA),
(Name: 'lightgray'; Value: $00D3D3D3),
(Name: 'lightgreen'; Value: $0090EE90),
(Name: 'lightgrey'; Value: $00D3D3D3),
(Name: 'lightpink'; Value: $00C1B6FF),
(Name: 'lightsalmon'; Value: $007AA0FF),
(Name: 'lightseagreen'; Value: $00AAB220),
(Name: 'lightskyblue'; Value: $00FACE87),
(Name: 'lightslategray'; Value: $00998877),
(Name: 'lightslategrey'; Value: $00998877),
(Name: 'lightsteelblue'; Value: $00DEC4B0),
(Name: 'lightyellow'; Value: $00E0FFFF),
(Name: 'lime'; Value: $0000FF00),
(Name: 'limegreen'; Value: $0032CD32),
(Name: 'linen'; Value: $00E6F0FA),
(Name: 'magenta'; Value: $00FF00FF),
(Name: 'maroon'; Value: $00000080),
(Name: 'mediumaquamarine'; Value: $00AACD66),
(Name: 'mediumblue'; Value: $00CD0000),
(Name: 'mediumorchid'; Value: $00D355BA),
(Name: 'mediumpurple'; Value: $00DB7093),
(Name: 'mediumseagreen'; Value: $0071B33C),
(Name: 'mediumslateblue'; Value: $00EE687B),
(Name: 'mediumspringgreen'; Value: $009AFA00),
(Name: 'mediumturquoise'; Value: $00CCD148),
(Name: 'mediumvioletred'; Value: $008515C7),
(Name: 'midnightblue'; Value: $00701919),
(Name: 'mintcream'; Value: $00FAFFF5),
(Name: 'mistyrose'; Value: $00E1E4FF),
(Name: 'moccasin'; Value: $00B5E4FF),
(Name: 'navajowhite'; Value: $00ADDEFF),
(Name: 'navy'; Value: $00800000),
(Name: 'oldlace'; Value: $00E6F5FD),
(Name: 'olive'; Value: $00008080),
(Name: 'olivedrab'; Value: $00238E6B),
(Name: 'orange'; Value: $0000A5FF),
(Name: 'orangered'; Value: $000045FF),
(Name: 'orchid'; Value: $00D670DA),
(Name: 'palegoldenrod'; Value: $00AAE8EE),
(Name: 'palegreen'; Value: $0098FB98),
(Name: 'paleturquoise'; Value: $00EEEEAF),
(Name: 'palevioletred'; Value: $009370DB),
(Name: 'papayawhip'; Value: $00D5EFFF),
(Name: 'peachpuff'; Value: $00B9DAFF),
(Name: 'peru'; Value: $003F85CD),
(Name: 'pink'; Value: $00CBC0FF),
(Name: 'plum'; Value: $00DDA0DD),
(Name: 'powderblue'; Value: $00E6E0B0),
(Name: 'purple'; Value: $00800080),
(Name: 'red'; Value: $000000FF),
(Name: 'rosybrown'; Value: $008F8FBC),
(Name: 'royalblue'; Value: $00E16941),
(Name: 'saddlebrown'; Value: $0013458B),
(Name: 'salmon'; Value: $007280FA),
(Name: 'sandybrown'; Value: $0060A4F4),
(Name: 'seagreen'; Value: $00578B2E),
(Name: 'seashell'; Value: $00EEF5FF),
(Name: 'sienna'; Value: $002D52A0),
(Name: 'silver'; Value: $00C0C0C0),
(Name: 'skyblue'; Value: $00EBCE87),
(Name: 'slateblue'; Value: $00CD5A6A),
(Name: 'slategray'; Value: $00908070),
(Name: 'slategrey'; Value: $00908070),
(Name: 'snow'; Value: $00FAFAFF),
(Name: 'springgreen'; Value: $007FFF00),
(Name: 'steelblue'; Value: $00B48246),
(Name: 'tan'; Value: $008CB4D2),
(Name: 'teal'; Value: $00808000),
(Name: 'thistle'; Value: $00D8BFD8),
(Name: 'tomato'; Value: $004763FF),
(Name: 'turquoise'; Value: $00D0E040),
(Name: 'violet'; Value: $00EE82EE),
(Name: 'wheat'; Value: $00B3DEF5),
(Name: 'white'; Value: $00FFFFFF),
(Name: 'whitesmoke'; Value: $00F5F5F5),
(Name: 'yellow'; Value: $0000FFFF),
(Name: 'yellowgreen'; Value: $0032CD9A)
);
var
NamedColorsDict: TDictionary<string, TColor>;
function TryGetColorName(const AColor: TColor; out AName: string): Boolean;
function TryStrToColor(const AStr: string; out AColor: TColor): Boolean;
function StrToColor(const AStr: string): TColor;
function ColorToHex(AColor: TColor): string;
function InvertColor(AColor: TColor): TColor; overload; inline;
function InvertColor(const AColor: TRGB): TRGB; overload; inline;
function InvertValue(const AColor: THSV): THSV; overload; inline;
function InvertValue(const AColor: TRGB): TRGB; overload; inline;
function InvertLightness(const AColor: THSL): THSL; overload; inline;
function InvertLightness(const AColor: TRGB): TRGB; overload; inline;
function Whiten(const AColor: TRGB): TRGB; inline;
function Darken(const AColor: TRGB): TRGB; inline;
function FadeToColor(const ABaseColor, ATargetColor: TRGB;
const AFraction: Double = 0.5): TRGB;
function ColorIsDark(AColor: TColor): Boolean;
function RBSwap(AColor: Integer): Integer; inline;
implementation
function rmod(const x, y: Double): Double; inline;
begin
Result := x - Floor(x / y) * y;
end;
function Fix360(const x: Double): Double; inline;
begin
Result := rmod(x, 360);
end;
function TRGB.Average: Double;
begin
Result := (Red + Green + Blue) / 3;
end;
constructor TRGB.Create(const ARed, AGreen, ABlue: Double);
begin
Red := ARed;
Green := AGreen;
Blue := ABlue;
end;
class operator TRGB.Implicit(const APascalColor: TColor): TRGB;
begin
Result.Red := (APascalColor and $000000FF) / 255;
Result.Green := ((APascalColor and $0000FF00) shr 8) / 255;
Result.Blue := ((APascalColor and $00FF0000) shr 16) / 255
end;
class operator TRGB.Equal(const AColor1, AColor2: TRGB): Boolean;
begin
Result := TColorComponents(AColor1) = TColorComponents(AColor2);
end;
class operator TRGB.NotEqual(const AColor1, AColor2: TRGB): Boolean;
begin
Result := not (AColor1 = AColor2)
end;
class operator TRGB.Add(const AColor1, AColor2: TRGB): TRGB;
begin
Result.Red := AColor1.Red + AColor2.Red;
Result.Green := AColor1.Green + AColor2.Green;
Result.Blue := AColor1.Blue + AColor2.Blue;
end;
class operator TRGB.Subtract(const AColor1, AColor2: TRGB): TRGB;
begin
Result.Red := AColor1.Red - AColor2.Red;
Result.Green := AColor1.Green - AColor2.Green;
Result.Blue := AColor1.Blue - AColor2.Blue;
end;
class operator TRGB.Multiply(const AFactor: Double; const AColor: TRGB): TRGB;
begin
Result.Red := AFactor * AColor.Red;
Result.Green := AFactor * AColor.Green;
Result.Blue := AFactor * AColor.Blue;
end;
class operator TRGB.Divide(const AColor: TRGB; const ADenominator: Double): TRGB;
begin
Result := (1 / ADenominator) * AColor;
end;
class operator TRGB.Implicit(const AColor: TRGB): TColor;
begin
with AColor do
Result := Round(255*Red) or (Round(255*Green) shl 8) or (Round(255*Blue) shl 16);
end;
function TRGB.Invert: TRGB;
begin
Result.Red := 1 - Red;
Result.Green := 1 - Green;
Result.Blue := 1 - Blue;
end;
function TRGB.IsValid: Boolean;
begin
Result := InRange(Red, 0, 1) and InRange(Green, 0, 1) and InRange(Blue, 0, 1);
end;
constructor THSL.Create(const AHue, ASaturation, ALightness: Double);
begin
Hue := Fix360(AHue);
Saturation := ASaturation;
Lightness := ALightness;
end;
class operator THSL.Implicit(const APascalColor: TColor): THSL;
begin
Result := THSL(TRGB(APascalColor));
end;
class operator THSL.Implicit(const AColor: THSL): TColor;
begin
Result := TColor(TRGB(AColor));
end;
class operator THSL.Explicit(const AColor: THSL): TRGB;
var
q, p, hk, tr, tg, tb: Double;
begin
with AColor, Result do
begin
if Lightness < 0.5 then
q := Lightness * (1 + Saturation)
else
q := Lightness + Saturation - (Lightness * Saturation);
p := 2 * Lightness - q;
hk := Hue / 360;
tr := hk + 1/3;
tg := hk;
tb := hk - 1/3;
if tr < 0 then tr := tr + 1;
if tg < 0 then tg := tg + 1;
if tb < 0 then tb := tb + 1;
if tr > 1 then tr := tr - 1;
if tg > 1 then tg := tg - 1;
if tb > 1 then tb := tb - 1;
if tr < 1/6 then
Red := p + ((q - p) * 6 * tr)
else if tr < 0.5 then
Red := q
else if tr < 2/3 then
Red := p + ((q - p) * 6 * (2/3 - tr))
else
Red := p;
if tg < 1/6 then
Green := p + ((q - p) * 6 * tg)
else if tg < 0.5 then
Green := q
else if tg < 2/3 then
Green := p + ((q - p) * 6 * (2/3 - tg))
else
Green := p;
if tb < 1/6 then
Blue := p + ((q - p) * 6 * tb)
else if tb < 0.5 then
Blue := q
else if tb < 2/3 then
Blue := p + ((q - p) * 6 * (2/3 - tb))
else
Blue := p;
end;
end;
class operator THSL.Equal(const AColor1, AColor2: THSL): Boolean;
begin
Result := TColorComponents(AColor1) = TColorComponents(AColor2);
end;
class operator THSL.NotEqual(const AColor1, AColor2: THSL): Boolean;
begin
Result := not (AColor1 = AColor2);
end;
class operator THSL.Explicit(const AColor: TRGB): THSL;
var
cmax, cmin, cdiff, csum: Double;
begin
cmax := TColorComponents(AColor).Max;
cmin := TColorComponents(AColor).Min;
cdiff := cmax - cmin;
csum := cmax + cmin;
with AColor, Result do
begin
if cmax = cmin then
Hue := 0
else if cmax = Red then
Hue := (60 * (Green - Blue) / cdiff)
else if cmax = Green then
Hue := (60 * (Blue - Red) / cdiff) + 120
else
Hue := (60 * (Red - Green) / cdiff) + 240;
Hue := Fix360(Hue);
if cmax = cmin then
Saturation := 0
else if csum <= 1 then
Saturation := cdiff / csum
else
Saturation := cdiff / (2 - csum);
Lightness := csum / 2;
end;
end;
function THSL.IsValid: Boolean;
begin
Result := InRange(Hue, 0, 360) and InRange(Saturation, 0, 1) and InRange(Lightness, 0, 1);
end;
constructor THSV.Create(const AHue, ASaturation, AValue: Double);
begin
Hue := Fix360(AHue);
Saturation := ASaturation;
Value := AValue;
end;
class operator THSV.Implicit(const APascalColor: TColor): THSV;
begin
Result := THSV(TRGB(APascalColor));
end;
class operator THSV.Implicit(const AColor: THSV): TColor;
begin
Result := TColor(TRGB(AColor));
end;
class operator THSV.Explicit(const AColor: THSV): TRGB;
var
hi: Integer;
f, q, p, t: Double;
begin
with AColor do
begin
hi := Floor(Hue / 60) mod 6;
f := Hue / 60 - floor(Hue / 60);
p := Value * (1 - Saturation);
q := Value * (1 - f * Saturation);
t := Value * (1 - (1 - f) * Saturation);
case hi of
0: Result := TRGB.Create(Value, t, p);
1: Result := TRGB.Create(q, Value, p);
2: Result := TRGB.Create(p, Value, t);
3: Result := TRGB.Create(p, q, Value);
4: Result := TRGB.Create(t, p, Value);
5: Result := TRGB.Create(Value, p, q);
end;
end;
end;
class operator THSV.Explicit(const AColor: TRGB): THSV;
var
cmax, cmin, cdiff: Double;
begin
cmax := TColorComponents(AColor).Max;
cmin := TColorComponents(AColor).Min;
cdiff := cmax - cmin;
with AColor, Result do
begin
if cmax = cmin then
Hue := 0
else if cmax = Red then
Hue := (60 * (Green - Blue) / cdiff)
else if cmax = Green then
Hue := (60 * (Blue - Red) / cdiff) + 120
else
Hue := (60 * (Red - Green) / cdiff) + 240;
Hue := Fix360(Hue);
if cmax = 0 then
Saturation := 0
else
Saturation := 1 - cmin / cmax;
Value := cmax;
end;
end;
class operator THSV.Equal(const AColor1, AColor2: THSV): Boolean;
begin
Result := TColorComponents(AColor1) = TColorComponents(AColor2);
end;
class operator THSV.NotEqual(const AColor1, AColor2: THSV): Boolean;
begin
Result := not (AColor1 = AColor2);
end;
function THSV.IsValid: Boolean;
begin
Result := InRange(Hue, 0, 360) and InRange(Saturation, 0, 1) and InRange(Value, 0, 1);
end;
class operator TColorComponents.Equal(const AColor1,
AColor2: TColorComponents): Boolean;
begin
Result := SameValue(AColor1.Components[0], AColor2.Components[0]) and
SameValue(AColor1.Components[1], AColor2.Components[1]) and
SameValue(AColor1.Components[2], AColor2.Components[2]);
end;
class operator TColorComponents.NotEqual(const AColor1,
AColor2: TColorComponents): Boolean;
begin
Result := not (AColor1 = AColor2);
end;
function TColorComponents.Max: Double;
begin
Result := MaxValue(Components);
end;
function TColorComponents.Min: Double;
begin
Result := MinValue(Components);
end;
function SameColor(AColor1, AColor2: TColor): Boolean;
begin
Result := (AColor1 and $00FFFFFF) = (AColor2 and $00FFFFFF);
end;
function RandomColor: TColor;
begin
Result := Random(256) or (Random(256) shl 8) or (Random(256) shl 16);
end;
function TryGetColorName(const AColor: TColor; out AName: string): Boolean;
var
i: Integer;
begin
for i := 0 to High(NamedColors) do
if NamedColors[i].Value = AColor then
begin
AName := NamedColors[i].Name;
Exit(True);
end;
AName := '';
Exit(False);
end;
function TryStrToColor(const AStr: string; out AColor: TColor): Boolean;
var
Str: string;
function OnlyHexDigitsAfterPrefix: Boolean;
var
i: Integer;
begin
for i := 2 to Str.Length do
if not CharInSet(Str[i], ['0'..'9', 'A'..'F', 'a'..'f']) then
Exit(False);
Result := True;
end;
var
IntVal: integer;
begin
Str := AStr.Trim;
if (Str.Length = 7) and (Str[1] = '#') and OnlyHexDigitsAfterPrefix then
begin
AColor := StrToInt('$' + Copy(Str, 2, 2)) or
(StrToInt('$' + Copy(Str, 4, 2)) shl 8) or
(StrToInt('$' + Copy(Str, 6, 2)) shl 16);
Exit(True);
end;
if TryStrToInt(Str, IntVal) then
begin
AColor := TColor(IntVal);
Exit(True);
end;
if NamedColorsDict.TryGetValue(Str, AColor) then
Exit(True);
Result := False;
end;
function StrToColor(const AStr: string): TColor;
begin
if not TryStrToColor(AStr, Result) then
raise EColorException.CreateFmt('Unsupported colour descriptor "%s".', [AStr]);
end;
function ColorToHex(AColor: TColor): string;
begin
AColor := ColorToRGB(AColor);
Result := Format('#%.2x%.2x%.2x',
[GetRValue(AColor), GetGValue(AColor), GetBValue(AColor)]);
end;
function InvertColor(AColor: TColor): TColor;
begin
Result := ColorToRGB(AColor) xor $FFFFFF;
end;
function InvertColor(const AColor: TRGB): TRGB;
begin
with AColor do
Result := TRGB.Create(1 - Red, 1 - Green, 1 - Blue);
end;
function InvertValue(const AColor: THSV): THSV;
begin
with AColor do
Result := THSV.Create(Hue, Saturation, 1 - Value);
end;
function InvertValue(const AColor: TRGB): TRGB;
begin
Result := TRGB(InvertValue(THSV(AColor)));
end;
function InvertLightness(const AColor: THSL): THSL;
begin
with AColor do
Result := THSL.Create(Hue, Saturation, 1 - Lightness);
end;
function InvertLightness(const AColor: TRGB): TRGB;
begin
Result := TRGB(InvertLightness(THSL(AColor)));
end;
function Whiten(const AColor: TRGB): TRGB;
begin
with AColor do
Result := TRGB.Create(
Red + (1 - Red) / 2,
Green + (1 - Green) / 2,
Blue + (1 - Blue) / 2
);
end;
function Darken(const AColor: TRGB): TRGB;
begin
with AColor do
Result := TRGB.Create(Red / 2, Green / 2, Blue / 2);
end;
function FadeToColor(const ABaseColor, ATargetColor: TRGB;
const AFraction: Double): TRGB;
begin
Result := TRGB.Create(
(1 - AFraction) * ABaseColor.Red + AFraction * ATargetColor.Red,
(1 - AFraction) * ABaseColor.Green + AFraction * ATargetColor.Green,
(1 - AFraction) * ABaseColor.Blue + AFraction * ATargetColor.Blue
);
end;
function ColorIsDark(AColor: TColor): Boolean;
begin
AColor := ColorToRGB(AColor);
Result := 0.299 * GetRValue(AColor) + 0.587 * GetGValue(AColor) + 0.114 * GetBValue(AColor) < 149;
end;
function RBSwap(AColor: Integer): Integer;
begin
Result := AColor and $FF00FF00 or Byte(AColor shr 16) or Byte(AColor) shl 16;
end;
function CompareColor(const A, B: THSV): TValueRelationship; overload;
begin
Result := CompareValue(A.Hue, B.Hue);
if Result = 0 then
Result := CompareValue(A.Saturation, B.Saturation);
if Result = 0 then
Result := CompareValue(A.Value, B.Value);
end;
function CompareColor(const A, B: TRGB): TValueRelationship; overload;
begin
Result := CompareColor(THSV(A), THSV(B));
end;
var
i: Integer;
initialization
NamedColorsDict := TDictionary<string, TColor>.Create;
for i := 0 to High(NamedColors) do
NamedColorsDict.Add(NamedColors[i].Name, NamedColors[i].Value);
finalization
NamedColorsDict.Free;
end.