(*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.php.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Andreas Schneider
 *)
unit UOverlayUI;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Gl, GLU, Imaging, ImagingTypes, ImagingClasses,
  ImagingOpenGL, OpenGLContext, ImagingUtility;
  
type

  { TGLArrow }

  TGLArrow = class(TObject)
    constructor Create(AGraphic: TSingleImage);
    destructor Destroy; override;
  protected
    FGraphic: TSingleImage;
    FTexture: GLuint;
    FRealWidth: Integer;
    FRealHeight: Integer;
    FWidth: Integer;
    FHeight: Integer;
    FCurrentX: Integer;
    FCurrentY: Integer;
    procedure UpdateTexture;
  public
    property Width: Integer read FWidth;
    property Height: Integer read FHeight;
    property CurrentX: Integer read FCurrentX;
    property CurrentY: Integer read FCurrentY;
  
    function HitTest(AX, AY: Integer): Boolean;
    procedure DrawGL(AX, AY: Integer; AActive: Boolean = False);
  end;
  
  { TOverlayUI }

  TOverlayUI = class(TObject)
    constructor Create;
    destructor Destroy; override;
  protected
    FArrows: array[0..7] of TGLArrow;
    FActiveArrow: Integer;
    FVisible: Boolean;
  public
    property ActiveArrow: Integer read FActiveArrow write FActiveArrow;
    property Visible: Boolean read FVisible write FVisible;
    function HitTest(AX, AY: Integer): Integer;
    procedure Draw(AContext: TOpenGLControl);
  end;

implementation

uses
  UResourceManager;

{ TGLArrow }

constructor TGLArrow.Create(AGraphic: TSingleImage);
var
  caps: TGLTextureCaps;
begin
  inherited Create;
  FRealWidth := AGraphic.Width;
  FRealHeight := AGraphic.Height;
  GetGLTextureCaps(caps);
  if caps.NonPowerOfTwo then
  begin
    FWidth := FRealWidth;
    FHeight := FRealHeight;
  end else
  begin
    if IsPow2(FRealWidth) then FWidth := FRealWidth else FWidth := NextPow2(FRealWidth);
    if IsPow2(FRealHeight) then FHeight := FRealHeight else FHeight := NextPow2(FRealHeight);
  end;
  FGraphic := TSingleImage.CreateFromParams(FWidth, FHeight, ifA8R8G8B8);
  AGraphic.CopyTo(0, 0, FRealWidth, FRealHeight, FGraphic, 0, 0);
  FTexture := 0;
end;

destructor TGLArrow.Destroy;
begin
  if FGraphic <> nil then FreeAndNil(FGraphic);
  if FTexture <> 0 then glDeleteTextures(1, @FTexture);
  inherited Destroy;
end;

procedure TGLArrow.UpdateTexture;
begin
  if (FGraphic <> nil) and (FRealWidth > 0) and (FRealWidth > 0) then
  begin
    FTexture := CreateGLTextureFromImage(FGraphic.ImageDataPointer^, 0, 0, False);

    glBindTexture(GL_TEXTURE_2D, FTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  end;
end;

function TGLArrow.HitTest(AX, AY: Integer): Boolean;
var
  pixel: TColor32Rec;
begin
  if (AX > -1) and (AX < FRealWidth) and (AY > -1) and (AY < FRealHeight) then
  begin
    pixel := GetPixel32(FGraphic.ImageDataPointer^, AX, AY);
    Result := pixel.A > 0;
  end else
    Result := False;
end;

procedure TGLArrow.DrawGL(AX, AY: Integer; AActive: Boolean = False);
begin
  FCurrentX := AX;
  FCurrentY := AY;

  if FTexture = 0 then UpdateTexture;

  if FTexture <> 0 then
  begin
    if AActive then
    begin
      glEnable(GL_COLOR_LOGIC_OP);
      glLogicOp(GL_COPY_INVERTED);
    end;

    glBindTexture(GL_TEXTURE_2D, FTexture);
    glBegin(GL_QUADS);
      glTexCoord2f(0, 0); glVertex2d(AX, AY);
      glTexCoord2f(1, 0); glVertex2d(AX + FWidth, AY);
      glTexCoord2f(1, 1); glVertex2d(AX + FWidth, AY + FHeight);
      glTexCoord2f(0, 1); glVertex2d(AX, AY + FHeight);
    glEnd;
    
    if AActive then
      glDisable(GL_COLOR_LOGIC_OP);
  end;
end;

{ TOverlayUI }

constructor TOverlayUI.Create;
var
  i: Integer;
  arrow: TSingleImage;
begin
  inherited Create;
  FActiveArrow := -1;
  FVisible := False;
  
  arrow := TSingleImage.CreateFromStream(ResourceManager.GetResource('Overlay/LeftTopArrow.tga'));
  for i := 0 to 3 do
  begin
    FArrows[2*i] := TGLArrow.Create(arrow);
    if i < 3 then
      arrow.Rotate(-90);
  end;
  arrow.Free;

  arrow := TSingleImage.CreateFromStream(ResourceManager.GetResource('Overlay/TopArrow.tga'));
  for i := 0 to 3 do
  begin
    FArrows[2*i+1] := TGLArrow.Create(arrow);
    if i < 3 then
      arrow.Rotate(-90);
  end;
  arrow.Free;
end;

destructor TOverlayUI.Destroy;
var
  i: Integer;
begin
  for i := 0 to 7 do
    if FArrows[i] <> nil then FreeAndNil(FArrows[i]);
    
  inherited Destroy;
end;

function TOverlayUI.HitTest(AX, AY: Integer): Integer;
var
  i: Integer;
begin
  Result := -1;
  i := 0;
  while (i <= 7) and (Result = -1) do
  begin
    if FArrows[i].HitTest(AX - FArrows[i].CurrentX, AY - FArrows[i].CurrentY) then
      Result := i;
    Inc(i);
  end;
end;

procedure TOverlayUI.Draw(AContext: TOpenGLControl);
begin
  if FVisible then
  begin
    glColor4f(1.0, 1.0, 1.0, 1.0);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    FArrows[0].DrawGL(10, 10, FActiveArrow = 0);
    FArrows[1].DrawGL(AContext.Width div 2 - FArrows[1].Width div 2, 10,
                      FActiveArrow = 1);
    FArrows[2].DrawGL(AContext.Width - 10 - FArrows[2].Width, 10,
                      FActiveArrow = 2);

    FArrows[3].DrawGL(AContext.Width - 10 - FArrows[3].Width,
                      AContext.Height div 2 - FArrows[3].Height div 2,
                      FActiveArrow = 3);

    FArrows[4].DrawGL(AContext.Width - 10 - FArrows[4].Width,
                      AContext.Height - 10 - FArrows[4].Height,
                      FActiveArrow = 4);
    FArrows[5].DrawGL(AContext.Width div 2 - FArrows[5].Width div 2,
                      AContext.Height - 10 - FArrows[5].Height,
                      FActiveArrow = 5);
    FArrows[6].DrawGL(10, AContext.Height - 10 - FArrows[6].Height,
                      FActiveArrow = 6);

    FArrows[7].DrawGL(10, AContext.Height div 2 - FArrows[7].Height div 2,
                      FActiveArrow = 7);
  end;
end;

end.

