FAQ » Grafika

Konwersja RGB na HSL i odwrotnie

Przedstawiony tu sposób zaokrągla trochę wartości, żeby obliczenia przechodziły szybciej. Jednak błędy wynikające z zaokrągleń są niewielkie i w większości zastosowań można ten sposób stosować.

uses
  Math;

const
  HLSMAX = 255;  (* Może to być dowolna inna wartość <= 255. Dobrze by było, żeby dzieliła się przez 6. Wówczas najmniejsze są błędy wynikające z zaokrągleń *)
  RGBMAX = 255;
(* Barwa (Hue) jest nieokreślona jeżeli Nasycenie (Saturation) jest róne 0 (odcienie szarości) *)
(* Ta wartość określa, wartość Hue, jeżeli kolor jest bezbarwny *)

  UNDEFINED = (HLSMAX*2 div 3);

procedure RGBtoHLS(RGBColor: TColor; var H, L, S: Byte);
var
 R, G, B: Byte;
 cMax, cMin: Byte; (* maksymalna i minimalna wartość RGB*)
 RDelta,GDelta,BDelta: Word; (* Wartości pośrednie: % wartości maksymalnej *)
begin
  (* wyciągnij wartości R, G, i B z koloru *)
  R := GetRValue(RGBColor);
  G := GetGValue(RGBColor);
  B := GetBValue(RGBColor);
  (* wylicz jasność *)
  cMax := Max(Max(R, G), B);
  cMin := Min(Min(R, G), B);
  L := (((cMax + cMin)*HLSMAX) + RGBMAX ) div (2*RGBMAX);
  if cMax = cMin then
  begin

  (* r=g=b --> przypadek bezbarwny *)
    S := 0;          (* Nasycenie (Saturation) *)
    H := UNDEFINED;  (* Barwa (Hue) *)
  end
  else
  begin

  (* przypadek barwny *)

    (* Nasycenie (Saturation) *)
    if L <= (HLSMAX div 2) then
      S := (((cMax - cMin)*HLSMAX) + ((cMax + cMin) div 2)) div (cMax + cMin)
    else
      S := (((cMax - cMin)*HLSMAX) + ((2*RGBMAX - cMax - cMin) div 2)) div (2*RGBMAX - cMax - cMin);
    (* Barwa (Hue) *)
      RDelta := (((cMax - R)*(HLSMAX div 6)) + ((cMax - cMin) div 2)) div (cMax - cMin);
      GDelta := (((cMax - G)*(HLSMAX div 6)) + ((cMax - cMin) div 2)) div (cMax - cMin);
      BDelta := (((cMax - B)*(HLSMAX div 6)) + ((cMax - cMin) div 2)) div (cMax - cMin);
    if R = cMax then
      H := BDelta - GDelta
    else
      if G = cMax then
        H := (HLSMAX div 3) + RDelta - BDelta
      else (* B = cMax *)
        H := ((2*HLSMAX) div 3) + GDelta - RDelta;
  end;
end;

function HLStoRGB(Hue, Lum{inosity}, Sat{uration}: Byte): TColor;

  function HueToRGB(n1, n2, Hue: Byte): Byte;
  begin
    Result := n1;
    (* Zwraca wartość R, G lub B *)
    if Hue < (HLSMAX div 6) then
    begin

      Result := (n1 + (((n2 - n1)*Hue + (HLSMAX div 12)) div (HLSMAX div 6)) );
      Exit;
    end;
    if Hue < (HLSMAX div 2) then
    begin

      Result := n2;
      Exit;
    end;
    if Hue < ((HLSMAX*2) div 3) then
    begin

      Result := (n1 + (((n2 - n1)*(((HLSMAX*2) div 3) - Hue)+(HLSMAX div 12)) div (HLSMAX div 6)));
      Exit;
    end;
  end;

var
  R, G, B: Byte;
  Magic1, Magic2: Word;       (* wylicza magiczne liczby *)
begin
  if Sat = 0 then
  begin

  (* przypadek bezbarwny *)
    B := (Lum*RGBMAX) div HLSMAX;
    R := B;
    G := B;
    if Hue < UNDEFINED </b>then
      raise</b> EConvertError.Create('Błędna wartość Hue');
  end
  else
  begin

  (* przypadek barwny *)

    (* wylicz magiczne liczby *)
    if Lum <= (HLSMAX div 2) then
      Magic2 := (Lum*(HLSMAX + Sat) + (HLSMAX div 2)) div HLSMAX
    else
      Magic2 := Lum + Sat - ((Lum*Sat) + (HLSMAX div 2)) div HLSMAX;
    Magic1 := 2*Lum - Magic2;

    (* Wylicz RGB; zmień HLSMAX na RGBMAX *)
    R := (HueToRGB(Magic1, Magic2, Hue + (HLSMAX div 3))*RGBMAX + (HLSMAX div 2)) div HLSMAX;
    G := (HueToRGB(Magic1, Magic2, Hue)*RGBMAX + (HLSMAX div 2)) div HLSMAX;
    B := (HueToRGB(Magic1, Magic2, Hue - (HLSMAX div 3))*RGBMAX + (HLSMAX div 2)) div HLSMAX;
  end;
  Result := RGB(R, G, B);
end;
</delphi>