{
   This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}
unit Umainhelper;

interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  StdCtrls, GR32_Image, GR32, gr32_transforms, GR32_Resamplers, GR32_Filters, math;

  procedure Effect_IncDecRGB(clip:tbitmap32;r0,g0,b0:integer);
  procedure Effect_IncDecCMYK(clip:tbitmap32;c2,m2,y2,k2:integer);
  procedure Resample(Src,Dst: TBitmap32; X,Y: Integer; Filter:TStretchFilter);
  procedure Brightness(src: TBitmap32; brightness:real);
  procedure RGBTOCMYK(R : byte; G : byte; B : byte;
                    var C : byte; var M : byte;
                    var Y : byte; var K : byte);

  procedure RGBtoHSL(RGB: TColor32; out H, S, L: Byte);
  procedure Effect_IncDecHSL(clip:tbitmap32;h0,s0,l0:integer);

implementation

function Max(const A, B, C: Integer): Integer;
asm
      CMP       EDX,EAX
      CMOVG     EAX,EDX
      CMP       ECX,EAX
      CMOVG     EAX,ECX
end;

function Min(const A, B, C: Integer): Integer;
asm
      CMP       EDX,EAX
      CMOVL     EAX,EDX
      CMP       ECX,EAX
      CMOVL     EAX,ECX
end;

function HSLtoRGB(H, S, L: Integer): TColor32;
var
  V, M, M1, M2, VSF: Integer;
begin
  if L <= $7F then
    V := L * (256 + S) shr 8
  else
    V := L + S - L * S div 255;
  if V <= 0 then
    Result := Color32(0, 0, 0, 0)
  else
  begin
    M := L * 2 - V;
    H := H * 6;
    VSF := (V - M) * (H and $ff) shr 8;
    M1 := M + VSF;
    M2 := V - VSF;
    case H shr 8 of
      0: Result := Color32(V, M1, M, 0);
      1: Result := Color32(M2, V, M, 0);
      2: Result := Color32(M, V, M1, 0);
      3: Result := Color32(M, M2, V, 0);
      4: Result := Color32(M1, M, V, 0);
      5: Result := Color32(V, M, M2, 0);
    end;
  end;
end;

procedure Effect_IncDecHSL(clip:tbitmap32;h0,s0,l0:integer);
var
p0:Pcolor32array;
x,y: Integer;
hd,sd,vd:byte;
SS:Pcolor32;
ps:TColor32; //singlepixel

begin
  SS := @clip.Bits[0];
  for y:=0 to clip.Height-1 do
  begin
    p0:=clip.scanline[y];
    for x:=0 to clip.Width-1 do
    begin
    RGBToHSL(p0[x],hd,sd,vd);
//    hd := ensurerange(hd + (h0),-180,180);
    sd := round(ensurerange(sd + (s0),0,255));
    vd := round(ensurerange(vd + (l0),0,255));
    hd := hd + (h0);
    if hd>255 then hd:=hd-255;
    if hd<0 then hd:=255-hd;


    ps:=HSLtoRGB(hd,sd,vd);
    SS^ := ps;
    inc(SS);
    end;
  end;
  clip.Changed;
end;

procedure RGBtoHSL(RGB: TColor32; out H, S, L: Byte);
var
  R, G, B, D, Cmax, Cmin, HL: Integer;
begin
  R := (RGB shr 16) and $ff;
  G := (RGB shr 8) and $ff;
  B := RGB and $ff;

  Cmax := Max(R, G, B);
  Cmin := Min(R, G, B);
  L := (Cmax + Cmin) div 2;

  if Cmax = Cmin then
  begin
    H := 0;
    S := 0
  end
  else
  begin
    D := (Cmax - Cmin) * 255;
    if L <= $7F then
      S := D div (Cmax + Cmin)
    else
      S := D div (255 * 2 - Cmax - Cmin);

    D := D * 6;
    if R = Cmax then
      HL := (G - B) * 255 * 255 div D
    else if G = Cmax then
      HL := 255 * 2 div 6 + (B - R) * 255 * 255 div D
    else
      HL := 255 * 4 div 6 + (R - G) * 255 * 255 div D;

    if HL < 0 then HL := HL + 255 * 2;
    H := HL;
  end;
end;

procedure CMYKTORGB(C : byte; M: byte;
                    Y : byte; K : byte;
                    var R : byte;
                    var G : byte;
                    var B : byte);
begin
   if (Integer(C) + Integer(K)) < 255 then
     R := 255 - (C + K) else
     R := 0;
   if (Integer(M) + Integer(K)) < 255 then
     G := 255 - (M + K) else
     G := 0;
   if (Integer(Y) + Integer(K)) < 255 then
     B := 255 - (Y + K) else
     B := 0;
end;

procedure RGBTOCMYK(R : byte; G : byte; B : byte;
                    var C : byte; var M : byte;
                    var Y : byte; var K : byte);
begin
  C := 255 - R;
  M := 255 - G;
  Y := 255 - B;
  if C < M then
    K := C else
    K := M;
  if Y < K then
    K := Y;
  if k > 0 then begin
    c := c - k;
    m := m - k;
    y := y - k;
  end;
end;

procedure Effect_IncDecRGB(clip:tbitmap32;r0,g0,b0:integer);
var
p0:Pcolor32array;
x,y:integer;
r,g,b: byte;
SS:Pcolor32;
begin
  SS := @clip.Bits[0];

  for y:=0 to clip.Height-1 do
  begin
    p0:=clip.scanline[y];
    for x:=0 to clip.Width-1 do
    begin
      b:=(p0[x] and $FF);
      g:=(p0[x] shr 8) and $FF;
      r:=(p0[x] shr 16) and $FF;
    r := EnsureRange(r+r0, 0,255);
    g := EnsureRange(g+g0, 0, 255);
    b := EnsureRange(b+b0, 0, 255);
    SS^ := $FF000000 + r shl 16 + g shl 8 + b;
    inc(SS);
    end;
  end;
  clip.Changed;
end;

procedure Effect_IncDecCMYK(clip:tbitmap32;c2,m2,y2,k2:integer);
var
p0:Pcolor32array;
x,y:integer;
r,g,b: byte;
SS:Pcolor32;
c0,m0,y0,k0,c1,m1,y1,k1:byte;
begin
  SS := @clip.Bits[0];

  for y:=0 to clip.Height-1 do
  begin
    p0:=clip.scanline[y];
    for x:=0 to clip.Width-1 do
    begin
      b:=(p0[x] and $FF);
      g:=(p0[x] shr 8) and $FF;
      r:=(p0[x] shr 16) and $FF;
    RGBTOCMYK(r,g,b,c0,m0,y0,k0);
    c1 := round(ensurerange(c0 + (c2),0,255));
    m1 := round(ensurerange(m0 + (m2),0,255));
    y1 := round(ensurerange(y0 + (y2),0,255));
    k1 := round(ensurerange(k0 + (k2),0,255));
    CMYKTORGB(c1,m1,y1,k1,r,g,b);
    SS^ := $FF000000 + r shl 16 + g shl 8 + b;
    inc(SS);
    end;
  end;
  clip.Changed;
end;

procedure Resample(Src,Dst: TBitmap32; X,Y: Integer; Filter:TStretchFilter);
var
  RectS: TRect;
  RectD: TRect;
  DstClipW,DstClipH:Trect;
  resampler:TCustomResampler;
Begin
  //(NearestFilter, LinearFilter,SplineFilter, LanczosFilter, MitchellFilter);
  RectS.Top := 0;
  RectS.Left := 0;
  RectS.Right := src.Width;
  RectS.Bottom := src.Height;
  RectD.Top := 0;
  RectD.Left := 0;
  RectD.Right := X;
  RectD.Bottom := Y;
  //DstClipW := DstClip.Right - DstClip.Left;
  //DstClipH := DstClip.Bottom - DstClip.Top;
  Dst.Clear(clGray32);
  Dst.Width:=X;
  Dst.Height:=Y;
  Src.DrawTo(dst,RectD,RectS);

  case Filter of
    sfNearest:resampler:=TNearestResampler.Create;
    sfDraft:resampler:=TDraftResampler.Create;
    sfLinear:resampler:=TLinearResampler.Create;
  else
    resampler:=TKernelResampler.Create;
    with resampler as TKernelResampler do
      case Filter of
        sfCosine: Kernel := TCosineKernel.Create;
        sfSpline: Kernel := TSplineKernel.Create;
        sfLanczos: Kernel := TLanczosKernel.Create;
        sfMitchell: Kernel := TMitchellKernel.Create;
      end;
  end;
  try
    StretchTransfer(Dst, RectD, RectD, Src, RectS, resampler, dmCustom, nil);
  finally
    resampler.Free;
  end;
end;

procedure Brightness(src: TBitmap32; brightness:real);
//scale from -1..1
var
 F: Single;
 I, V: Integer;
 LUT: TLUT8;
  dst:tbitmap32;
 w,h:integer;
 begin
  dst:=tbitmap32.create;
  w:=src.width;
  h:=src.height;
  dst.width:=w;
  dst.height:=h;
 for I := 0 to 255 do
 begin
 F := I / 255; // Scale to 0..1 range

// F := Power(F, GAMMA); // Gamma
// F := (F - 0.5) * (1 + CONTRAST) + 0.5; // Contrast
 F := F + BRIGHTNESS; // Brightness

 V := Round(F * 255); // Scale to 0..255 range

 if V < 0 then V := 0
 else if V > 255 then V := 255; // Clip to 0..255 range

 LUT[I] := V;
 end;
 ApplyLut(dst, src, LUT);
   src.assign(dst);
   src.changed;
 dst.free;
end;

end.
