unit mbColorConv; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Graphics; type TBrightnessMode = (bmLuminance, bmValue); { HSL color model } function HSLtoColor(H, S, L: double): TColor; procedure HSLtoRGB(H, S, L: Double; out R, G, B: Integer); procedure ColortoHSL(c: TColor; out H, S, L: Double); procedure RGBtoHSL(R, G, B: Integer; out H, S, L: Double); { HSV color model } procedure ColorToHSV(c: TColor; out H, S, V: Double); procedure RGBtoHSV(R, G, B: Integer; out H, S, V: Double); function HSVtoColor(H, S, V: Double): TColor; procedure HSVtoRGB(H, S, V: Double; out R, G, B: Integer); implementation uses Math, LclIntf; function modulo(x, y: Double): Double; begin Result := x - floor(x / y) * y; end; //============================================================================== // HSL color model //============================================================================== function HSLToColor(H, S, L: Double): TColor; var R, G, B: Integer; begin HSLtoRGB(H, S, L, R, G, B); Result := RGBtoColor(R, G, B); end; (* procedure HSLtoRGB(H, S, L: double; out R, G, B: Integer); var C, X, m: Double; rr, gg, bb: Double; begin H := H * 360; C := (1 - abs(2*L - 1)) * S; X := C * (1 - abs(modulo(H / 60, 2) - 1)); m := L - C/2; if H < 60 then begin R := round((C + m) * 255); G := round((X + m) * 255); B := round(m * 255); end else if H < 120 then begin R := round((X + m) * 255); G := round((C + m) * 255); B := round(m * 255); end else if H < 180 then begin R := round(m * 255); G := round((C + m) * 255); B := round((X + m) * 255); end else if H < 240 then begin R := round(m * 255); G := round((X + m) * 255); B := round((C + m) * 255); end else if H < 300 then begin R := round((X + m) * 255); G := round(m * 255); B := round((C + m) * 255); end else begin R := round((C + m) * 255); G := round(m * 255); B := round((X + m) * 255); end; end; *) procedure HSLtoRGB(H, S, L: double; out R, G, B: Integer); var M1, M2: double; function HueToColorValue(Hue: double): byte; var V : double; begin if Hue > 10 then Hue := Hue + 1; if Hue < 0 then Hue := Hue + 1 else if Hue > 1 then Hue := Hue - 1; if 6 * Hue < 1 then V := M1 + (M2 - M1) * Hue * 6 else if 2 * Hue < 1 then V := M2 else if 3 * Hue < 2 then V := M1 + (M2 - M1) * (2/3 - Hue) * 6 else V := M1; Result := round(255 * V) end; begin if S = 0 then begin R := round(255 * L); G := R; B := R end else begin if L <= 0.5 then M2 := L * (1 + S) else M2 := L + S - L * S; M1 := 2 * L - M2; R := HueToColorValue(H + 1/3); G := HueToColorValue(H); B := HueToColorValue(H - 1/3) end; end; procedure ColorToHSL(c: TColor; out H, S, L: Double); begin RGBtoHSL(GetRValue(c), GetGValue(c), GetBValue(c), H, S, L); end; // From: http://www.rapidtables.com/convert/color/rgb-to-hsl.htm procedure RGBtoHSL(R, G, B: Integer; out H, S, L: Double); var rr, gg, bb, Cmax, Cmin, delta: double; begin rr := R / 255; gg := G / 255; bb := B / 255; Cmax := MaxValue([rr, gg, bb]); Cmin := MinValue([rr, gg, bb]); delta := (Cmax - Cmin); if delta = 0 then begin H := 0; S := 0; end else begin // Calculate L L := (Cmax + Cmin) / 2; // Calculate H if Cmax = rr then begin H := modulo((gg - bb) / delta, 6); { H := ((gg - bb) / delta); H := H - floor(H / 6); } H := H * 60; end else if Cmax = gg then H := 60 * ((bb - rr) / delta + 2) else if Cmax = bb then H := 60 * ((rr - gg) / delta + 4) else H := 0; H := H / 360; // Calculate S S := delta / (1 - abs(2 * L - 1)); end; end; (* procedure RGBtoHSL(R, G, B: Integer; out H, S, L: Double); var rr, gg, bb, D, Cmax, Cmin: double; begin rr := R / 255; gg := G / 255; bb := B / 255; Cmax := MaxValue([rr, gg, bb]); Cmin := MinValue([rr, gg, bb]); L := (Cmax + Cmin) / 2; if Cmax = Cmin then begin H := 0; S := 0; end else begin D := Cmax - Cmin; //calc S if L < 0.5 then S := D / (Cmax + Cmin) else S := D / (2 - Cmax - Cmin); //calc H if R = Cmax then H := (gg - bb) / D else if G = Cmax then H := 2 + (bb - rr) /D else H := 4 + (rr - gg) / D; H := H / 6; if H < 0 then H := H + 1; end; end; *) //============================================================================== // HSV color model //============================================================================== { Assumes H, S, V in the range 0..1 and calculates the R, G, B values which are returned to be in the range 0..255. From: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c } procedure HSVtoRGB(H, S, V: Double; out R, G, B: Integer); var i: Integer; f: Double; p, q, t: Double; procedure MakeRgb(rr, gg, bb: Double); begin R := Round(rr * 255); G := Round(gg * 255); B := Round(bb * 255); end; begin i := floor(H * 6); f := H * 6 - i; p := V * (1 - S); q := V * (1 - f*S); t := V * (1 - (1 - f) * S); case i mod 6 of 0: MakeRGB(V, t, p); 1: MakeRGB(q, V, p); 2: MakeRGB(p, V, t); 3: MakeRGB(p, q, V); 4: MakeRGB(t, p, V); 5: MakeRGB(V, p, q); else MakeRGB(0, 0, 0); end; end; function HSVToColor(H, S, V: Double): TColor; var r, g, b: Integer; begin HSVtoRGB(H, S, V, r, g, b); Result := RgbToColor(r, g, b); end; { Assumes R, G, B to be in range 0..255. Calculates H, S, V in range 0..1 From: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c } procedure ColorToHSV(c: TColor; out H, S, V: Double); begin RGBToHSV(GetRValue(c), GetGValue(c), GetBValue(c), H, S, V); end; procedure RGBToHSV(R, G, B: Integer; out H, S, V: Double); var rr, gg, bb: Double; cmax, cmin, delta: Double; begin rr := R / 255; gg := G / 255; bb := B / 255; cmax := MaxValue([rr, gg, bb]); cmin := MinValue([rr, gg, bb]); delta := cmax - cmin; if delta = 0 then begin H := 0; S := 0; end else begin if cmax = rr then H := (gg - bb) / delta + IfThen(gg < bb, 6, 0) else if cmax = gg then H := (bb - rr) / delta + 2 else if (cmax = bb) then H := (rr -gg) / delta + 4; H := H / 6; S := delta / cmax; end; V := cmax; end; end.