Search code examples
inno-setuppercentagepascalscripttcolor

How to Lighten or Darken a specified TColor in Inno Setup Pascal Script?


I need to make color of my Status Bar (it is a TPanel) change (Lighten or Darken) automatically according to user's current System Specifications which is displayed in my wpInfoBefore Wizard Page.

I like to have two functions which can do this correctly by inputting a TColor as Value. But , I tried many times to write those functions by reading posts like this , even using RGB function, but with no success.

For Example, If I need to darken or lighten the given TColor, I may need to use functions like shown below:

var
   RecommendedStatusColor: TColor;

function LightenColor(Colour: TColor, Percentage: Integer): TColor;
begin 
   ...
end;

function DarkenColor(Colour: TColor, Percentage: Integer): TColor;
begin 
   ...
end;    

RecommendedStatusColor := $00D000;

if ... then
StatusBar.Color := LightenColor(RecommendedStatusColor, 75);
//Lighten given color by 75%

if ... then
StatusBar.Color := DarkenColor(RecommendedStatusColor, 50);
//Darken given color by 50%

Output should be the modified (Lightened or Darkened) TColor.

Thanks in Advance.


Solution

  • You have to convert the color to HSL or HSV and change the lightness (L) or value (V) and convert back to RGB.

    The following code uses the HSL (L = lightness).

    function GetRValue(RGB: Cardinal): Byte;
    begin
      Result := Byte(rgb);
    end;
    
    function GetGValue(RGB: Cardinal): Byte;
    begin
      Result := Byte(rgb shr 8);
    end;
    
    function GetBValue(RGB: Cardinal): Byte;
    begin
      Result := Byte(rgb shr 16);
    end;
    
    function Max(A, B: Integer): Integer;
    begin
      if A > B then
        Result := A
      else
        Result := B;
    end;
    
    function Min(A, B: Integer): Integer;
    begin
      if A < B then
        Result := A
      else
        Result := B;
    end;
    
    const
      HLSMAX = 240;
      RGBMAX = 255;
      HLSUndefined = (HLSMAX*2/3);
      
    procedure ColorRGBToHLS(RGB: Cardinal; var Hue, Luminance, Saturation: Word);
    var
      H, L, S: Double;
      R, G, B: Word;
      cMax, cMin: Double;
      Rdelta, Gdelta, Bdelta: Word; // intermediate value: % of spread from max
    begin
      R := GetRValue(RGB);
      G := GetGValue(RGB);
      B := GetBValue(RGB);
    
      // calculate lightness
      cMax := Max(Max(R, G), B);
      cMin := Min(Min(R, G), B);
      L := ( ((cMax + cMin) * HLSMAX) + RGBMAX ) / ( 2 * RGBMAX);
      Luminance := Trunc(L);
      if cMax = cMin then // r=g=b --> achromatic case
      begin                
        Hue := Trunc(HLSUndefined);
        Saturation := 0;
      end
      else                 // chromatic case
      begin
        // saturation
        if Luminance <= HLSMAX/2 then
        begin
          S := ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
        end
          else
        begin
          S := ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) ) /
               (2*RGBMAX-cMax-cMin);
        end;
    
        // hue
        Rdelta := Trunc(( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin));
        Gdelta := Trunc(( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin));
        Bdelta := Trunc(( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin));
        
        if (Double(R) = cMax) then
        begin
          H := Bdelta - Gdelta
        end
        else if (Double(G) = cMax) then
        begin
          H := (HLSMAX/3) + Rdelta - Bdelta
        end
        else // B == cMax
        begin
          H := ((2 * HLSMAX) / 3) + Gdelta - Rdelta;
        end;
    
        if (H < 0) then
          H := H + HLSMAX;
        if (H > HLSMAX) then
          H := H - HLSMAX;
    
        Hue := Round(H);
        Saturation := Trunc(S);
      end;
    end;
    
    function HueToRGB(Lum, Sat, Hue: Double): Integer;
    var
      ResultEx: Double;
    begin
      // range check: note values passed add/subtract thirds of range
      if (hue < 0) then
         hue := hue + HLSMAX;
    
      if (hue > HLSMAX) then
         hue := hue - HLSMAX;
    
      // return r,g, or b value from this tridrant
      if (hue < (HLSMAX/6)) then
        ResultEx := Lum + (((Sat-Lum)*hue+(HLSMAX/12))/(HLSMAX/6))
      else if (hue < (HLSMAX/2)) then
        ResultEx := Sat
      else if (hue < ((HLSMAX*2)/3)) then
        ResultEx := Lum + (((Sat-Lum)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6))
      else
        ResultEx := Lum;
      Result := Round(ResultEx);
    end;
    
    function RoundColor(Value: Double): Integer;
    begin
      if Value > 255 then
        Result := 255
      else
        Result := Round(Value);
    end;
    
    function RGB(R, G, B: Byte): Cardinal;
    begin
      Result := (Cardinal(R) or (Cardinal(G) shl 8) or (Cardinal(B) shl 16));
    end;
    
    function ColorHLSToRGB(Hue, Luminance, Saturation: Word): Cardinal;
    var
      R, G, B: Double;              // RGB component values
      Magic1, Magic2: Double;       // calculated magic numbers (really!)
    begin
      if (Saturation = 0) then
      begin            // achromatic case
         R := (Luminance * RGBMAX)/HLSMAX;
         G := R;
         B := R;
         if (Hue <> HLSUndefined) then
           ; // ERROR
      end
      else
      begin            // chromatic case
         // set up magic numbers
         if (Luminance <= (HLSMAX/2)) then
         begin
           Magic2 := (Luminance * (HLSMAX + Saturation) + (HLSMAX/2)) / HLSMAX;
         end
           else
         begin
           Magic2 :=
             Luminance + Saturation - ((Luminance * Saturation) +
             (HLSMAX/2)) / HLSMAX;
         end;
         Magic1 := 2 * Luminance - Magic2;
    
         // get RGB, change units from HLSMAX to RGBMAX
         R := (HueToRGB(Magic1,Magic2,Hue+(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX;
         G := (HueToRGB(Magic1,Magic2,Hue)*RGBMAX + (HLSMAX/2)) / HLSMAX;
         B := (HueToRGB(Magic1,Magic2,Hue-(HLSMAX/3))*RGBMAX + (HLSMAX/2))/HLSMAX;
      end;
      Result := RGB(RoundColor(R), RoundColor(G), RoundColor(B));
    end;
    
    function LightenColor(RGB: Cardinal; Percentage: Integer): Cardinal;
    var
      H, S, L: Word; 
    begin
      ColorRGBToHLS(RGB, H, L, S); 
      L := (Cardinal(L) * Percentage) div 100;
      Result := ColorHLSToRGB(H, L, S); 
    end;
    
    function GetSysColor(nIndex: Integer): DWORD;
      external '[email protected] stdcall';
    
    function ColorToRGB(Color: TColor): Cardinal;
    begin
      if Color < 0 then
        Result := GetSysColor(Color and $000000FF) else
        Result := Color;
    end;
    

    Usage:

    LighterColor := TColor(LightenColor(ColorToRGB(Color), 150));
    DarkerColor := TColor(LightenColor(ColorToRGB(Color), 75));
    

    Lighter/darker color

    References: