unit dgui_skinatlas;

{$MODE FPC}
{$MODESWITCH AUTODEREF}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}

interface

uses
  dvector2i,
  dvector2f,
  drect2i,
  drect2f,
  dcolors,
  dstream,
  deng,
  DDrawTex,
  DDrawFont,
  dgui3;

type
PGuiStyleSkinAtlas = ^TGuiStyleSkinAtlas;
PGuiThemeSkinAtlas = ^TGuiThemeSkinAtlas;

TGuiStyleSkinAtlas = object(TGuiStyle)
private
  FTheme: PGuiThemeSkinAtlas;
public
  constructor Init(const aWidgetName: AnsiString; Theme: PGuiThemeSkinAtlas);

  procedure ButtonRender(Widget: PWidget; ButtonState: PGUIButtonState); virtual;
end;

TGuiThemeSkinAtlas = object(TGuiTheme)
private
  FFont: TFont;
  FAtlas: TDrawTex;
  FDefault: TGuiStyleSkinAtlas;
  FButtonRect: TRect2f;
  FButtonBorders: TVec2f;
public
  constructor Init(FontData: TData;
                   AtlasData: TData);
  destructor Done; virtual;
end;

implementation

constructor TGuiStyleSkinAtlas.Init(const aWidgetName: AnsiString; Theme: PGuiThemeSkinAtlas);
begin
  inherited Init(aWidgetName);
  FTheme := Theme;
end;

procedure DrawSlice9(const Tex: TDrawTex;
                     const Pos, Size: TVec2f;
                     const TexPos, TexSize: TVec2f;
                     const BL, BR, BU, BD: Single);
var
  P, S: TVec2f;
  H, V: array[0 .. 2] of Single;
  F: TVec2f;
  T: TVec2f;
begin
  F := Vec2f(Tex.Size);
  P := Pos;
  H[0] := BL;
  H[1] := TexSize.X - BL - BR;
  H[2] := BR;
  V[0] := BU;
  V[1] := TexSize.Y - BU - BD;
  V[2] := BD;
  // LT
  Tex.DrawLT(@Eng, P, Vec2f(BL, BU),
      Vec2f(TexPos.X / F.X, TexPos.Y / F.Y), Vec2f(BL / F.X, BU / F.Y));
  // RT
  Tex.DrawLT(@Eng, P + Vec2f(Size.X - BR, 0), Vec2f(BR, BU),
      Vec2f((TexPos.X + TexSize.X - BR) / F.X, TexPos.Y / F.Y), Vec2f(BR / F.X, BU / F.Y));
  // LB
  Tex.DrawLT(@Eng, P + Vec2f(0, Size.Y - BD), Vec2f(BL, BD),
      Vec2f(TexPos.X / F.X, (TexPos.Y + TexSize.Y - BD) / F.Y), Vec2f(BL / F.X, BD / F.Y));
  // RB
  Tex.DrawLT(@Eng, P + Vec2f(Size.X - BR, Size.Y - BD), Vec2f(BR, BD),
      Vec2f((TexPos.X + TexSize.X - BR) / F.X, (TexPos.Y + TexSize.Y - BD) / F.Y), Vec2f(BR / F.X, BD / F.Y));

  //Eng.Draw.Color(RGBA($00, $ff, $00, $ff));
  //Eng.Draw.Rect(Rect2f(P, Vec2f(BL, BU)));
  //Eng.Draw.Rect(Rect2f(P + Vec2f(Size.X - BR, 0), Vec2f(BR, BU)));
  //Eng.Draw.Rect(Rect2f(P + Vec2f(0, Size.Y - BD), Vec2f(BL, BD)));
  //Eng.Draw.Rect(Rect2f(P + Vec2f(Size.X - BR, Size.Y - BD), Vec2f(BR, BD)));

  // Top and bottom strips
  P := Pos + Vec2f(BL, 0);
  S := Vec2f(H[1], BU);
  T := TexPos + Vec2f(BL, 0);
  if P.X < Pos.X + Size.X - BR then begin
    while P.X < Pos.X + Size.X - BR do begin
      if P.X + H[1] >= Pos.X + Size.X - BR then begin
        S.X := Pos.X + Size.X - BR - P.X;
      end;
      Tex.DrawLT(@Eng, P, Vec2f(S.X, BU),
         Vec2f(T.X / F.X, T.Y / F.Y), Vec2f(S.X / F.X, BU / F.Y));
      Tex.DrawLT(@Eng, P + Vec2f(0, Size.Y - BD), Vec2f(S.X, BD),
         Vec2f(T.X / F.X, (T.Y + TexSize.Y - BD) / F.Y), Vec2f(S.X / F.X, BD / F.Y));
      // Eng.Draw.Color(RGBA($00, $ff, $00, $ff));
      // Eng.Draw.Rect(Rect2f(P, Vec2f(S.X, BU)));
      // Eng.Draw.Rect(Rect2f(P + Vec2f(0, Size.Y - BD), Vec2f(S.X, BD)));
      // Eng.Draw.Color(RGBA($ff, $ff, $ff, $ff));
      P.X := P.X + S.X;
    end;
  end;

  // Left and right strips
  P := Pos + Vec2f(0, BU);
  S := Vec2f(BL, V[1]);
  T := TexPos + Vec2f(0, BU);
  if P.Y < Pos.Y + Size.Y - BD then begin
    while P.Y < Pos.Y + Size.Y - BD do begin
      if P.Y + V[1] >= Pos.Y + Size.Y - BD then begin
        S.Y := Pos.Y + Size.Y - BD - P.Y;
      end;
      Tex.DrawLT(@Eng, P, Vec2f(BL, S.Y),
         Vec2f(T.X / F.X, T.Y / F.Y), Vec2f(BL / F.X, S.Y / F.Y));
      Tex.DrawLT(@Eng, P + Vec2f(Size.X - BR, 0), Vec2f(BR, S.Y),
         Vec2f((T.X + TexSize.X - BR - 1) / F.X, T.Y / F.Y), Vec2f(BR / F.X, S.Y / F.Y));
      P.Y := P.Y + S.Y;
    end;
  end;
end;

procedure DrawStretchedHorizontal(const Tex: TDrawTex;
                     const Pos, Size: TVec2f;
                     const TexPos, TexSize: TVec2f;
                     const BL, BR: Single);
//
//      Texture part:                           Output:
//
//     +--+-------+--+          +--+-------+-------+-------+-------+--+
//     |..|#######|..|          |..|#######|#######|#######|#######|..|
//     |..|#######|..|   ->     |..|#######|#######|#######|#######|..|
//     |..|#######|..|          |..|#######|#######|#######|#######|..|
//     +--+-------+--+          +--+-------+-------+-------+-------+--+
//
var
  P, S: TVec2f;
  H, V: array[0 .. 2] of Single;
  F: TVec2f;
  T: TVec2f;
begin
  F := Vec2f(Tex.Size);
  P := Pos;
  H[0] := BL;
  H[1] := TexSize.X - BL - BR;
  H[2] := BR;
  // Left
  Tex.DrawLT(@Eng, P, Vec2f(BL, Size.Y),
      Vec2f(TexPos.X / F.X, TexPos.Y / F.Y), Vec2f(BL / F.X, TexSize.Y / F.Y));
  // Right
  Tex.DrawLT(@Eng, P + Vec2f(Size.X - BR, 0), Vec2f(BR, Size.Y),
      Vec2f((TexPos.X + TexSize.X - BR) / F.X, TexPos.Y / F.Y), Vec2f(BR / F.X, TexSize.Y / F.Y));

  // Internal
  P := Pos + Vec2f(BL, 0);
  S := Vec2f(H[1], 0);
  T := TexPos + Vec2f(BL, 0);
  if P.X < Pos.X + Size.X - BR then begin
    while P.X < Pos.X + Size.X - BR do begin
      if P.X + H[1] >= Pos.X + Size.X - BR then begin
        S.X := Pos.X + Size.X - BR - P.X;
      end;
      Tex.DrawLT(@Eng, P, Vec2f(S.X, Size.Y),
         Vec2f(T.X / F.X, T.Y / F.Y), Vec2f(S.X / F.X, TexSize.Y / F.Y));
      P.X := P.X + S.X;
    end;
  end;
end;

procedure TGuiStyleSkinAtlas.ButtonRender(Widget: PWidget; ButtonState: PGUIButtonState);
var
  LeftBorder: Single;
  RightBorder: Single;
begin
  LeftBorder := FTheme^.FButtonBorders.X;
  RightBorder := FTheme^.FButtonBorders.Y;
  Eng.Draw.Color(RGBA($ff, $ff, $ff, $ff));
  //FTheme^.FAtlas.Draw(@Eng, Vec2f(Widget^.GlobalPos) + Mul2f(Vec2f(Widget^.Size), 0.5), Vec2f(Widget^.Size),
  //                    Vec2f(FTheme^.FButtonRect.P.X / FTheme^.FAtlas.Size.X, FTheme^.FButtonRect.P.Y / FTheme^.FAtlas.Size.Y),
  //                    Vec2f(FTheme^.FButtonRect.S.X / FTheme^.FAtlas.Size.X, FTheme^.FButtonRect.S.Y / FTheme^.FAtlas.Size.Y));
  //DrawSlice9(FTheme^.FAtlas, Vec2f(Widget^.GlobalPos), Vec2f(Widget^.Size),
  //           FTheme^.FButtonRect.P, FTheme^.FButtonRect.S,
  //           LeftBorder, RightBorder, LeftBorder, RightBorder);
  DrawStretchedHorizontal(FTheme^.FAtlas,
    Vec2f(Widget^.GlobalPos), Vec2f(Widget^.Size),
    FTheme^.FButtonRect.P, FTheme^.FButtonRect.S,
    LeftBorder, RightBorder);
end;

function EndsWith(const S, E: AnsiString): Boolean;
begin
  if Length(S) < Length(E) then
    Exit(False);
  Result := Copy(S, Length(S) - Length(E) + 1, Length(E)) = E;
end;

constructor TGuiThemeSkinAtlas.Init(FontData: TData;
                 AtlasData: TData);
begin
  if EndsWith(FontData.Stream^.Name, '.bit') then begin
    // legacy -- much old code rely on this
    FFont.InitCP1251(nil, @Eng, FontData);
  end else
    FFont.InitFromJson(nil, @Eng, FontData);
  FAtlas.Init(nil, @Eng, AtlasData);
  AddFont('default', @FFont);
  FDefault.Init(STYLE_DEFAULT, @Self);
  AddStyle(STYLE_DEFAULT, @FDefault);

  FButtonRect.P := Vec2f(202, 29);
  FButtonRect.S := Vec2f(27, 24);
  FButtonBorders := Vec2f(7, 7);
end;

destructor TGuiThemeSkinAtlas.Done;
begin
  FAtlas.Done;
  FFont.Done;
  inherited Done;
end;

end.
