//----------------------------------------------------------------------------
//
// Author        : Johannes Stein
// Email         : johannesstein@freeze-dev.de
// Website       : http://www.freeze-dev.de
// Version 	     : 1.30
// Last modified : 10-03-22
// 
// Description   : Inspired by Jan Horn's Textures.pas I wrote a little
//				   texture loader for SDL + OpenGL to load images or 
//                 SDL surfaces and bind them to an OpenGL object
//
// License       : MIT license
//			       For more information see 
//				     http://www.opensource.org/licenses/mit-license.php
//
// What is needed before using SDLTextures.pas?
//   dglOpenGL (OpenGL header): http://www.delphigl.com
//     You can also use the old OpenGL headers from Delphi or similar
//     OpenGL headers. Just modify the uses clause.
//   SDL headers: http://jedi-sdl.pascalgamedevelopment.com
//     You only need these if you're using Delphi or FreePascal < 2.2.2
//
// Usage         : Load your texture
//				   from file: LoadTexture(Filename, Texture)
//					     e.g. LoadTexture('mytexture.png', MyTexture)
//				   from surface: LoadTexture(Surface, Texture)
//					  	    e.g. LoadTexture(MySurface, MyTexture)
//
//				   After your texture has been loaded you can
//				   bind it on your object with either Bind(Texture)
//				   or glBindTexture(GL_TEXTURE_2D, Texture)
//
//				   Sine qua non: You need the line
//                 glEnable(GL_TEXTURE_2D)
//                 somewhere in your code or the textures won't be displayed 
//
//----------------------------------------------------------------------------

unit SDLTextures;

{$I ElysionKronos.inc}
{$mode delphi}

interface

uses 
	SDL,
	SDLUtils,
	{$IFDEF USE_SDL_IMAGE}
	SDL_Image,
	{$ENDIF}
	{$IFDEF USE_VAMPYRE}
	ImagingSDL,
	{$ENDIF}
	{$IFDEF USE_DGL_HEADER}
    	dglOpenGL,
    {$ELSE}
    	gl, glu, glext,
    {$ENDIF}
	SysUtils;

// Returns the next power of 2
function PowerOfTwo(Input: Integer): Integer;
	
function LoadTexture(Filename: String; var Texture: GLuint): Boolean; Overload;
function LoadTexture(Surface: PSDL_Surface; var Texture: GLuint): Boolean; Overload;
procedure SwapBGR(SDL_Surface: PSDL_Surface);

procedure Bind(var Texture: GLuint); 

implementation

function SupportsNonPowerOfTwo: Boolean;
begin
  if AnsiPos('GL_ARB_texture_non_power_of_two', glGetString(GL_EXTENSIONS)) <> 0 then Result := true else Result := false;
end;

(*
 * See for more information: http://www.gamedev.net/community/forums/topic.asp?topic_id=284259
 *)
function PowerOfTwo(Input: Integer): Integer;
var
  Value: Integer = 1;
begin
  while ( Value < Input ) do Value := Value shl 1;
  Result := Value;
end;

// http://osdl.sourceforge.net/main/documentation/rendering/SDL-openGL-examples.html
function CreateTexture(SDL_Surface: PSDL_Surface; BuildMipMap: Boolean = false): GLuInt;
var
  NewSurface: PSDL_Surface;
  Texture: GLuInt;
  SrfWidth, SrfHeight: Integer;
  area: TSDL_Rect;
  colorkey: UInt32;
  clrk_r, clrk_g, clrk_b: PUInt8;
  rmask, gmask, bmask, amask: UInt32;
  saved_flags: UInt32;
  saved_alpha: UInt8;
Begin
  Result := -1;

  if Assigned(SDL_Surface) then
  begin

    // Set up Power-of-two width and height for mipmaps
    SrfWidth := PowerOfTwo(SDL_Surface^.w);
    SrfHeight := PowerOfTwo(SDL_Surface^.h);

    // Set up mask for new surface
    if SDL_BYTEORDER = SDL_LIL_ENDIAN then
    begin
      rmask := $000000FF;
      gmask := $0000FF00;
      bmask := $00FF0000;
      amask := $FF000000;
    end else
    begin
      rmask := $FF000000;
      gmask := $00FF0000;
      bmask := $0000FF00;
      amask := $000000FF;
    end;

    // Create new surface, quit and show error if it fails
    NewSurface := SDL_CreateRGBSurface(SDL_SWSURFACE, SDL_Surface^.w, SDL_Surface^.h, 32,  rmask, gmask, bmask, amask);

    if (NewSurface = nil) then
    begin
      raise Exception.Create('SDLTextures: Could not create RGB Surface');
      Exit;
    end;


    // Alpha stuff, see: http://twomix.devolution.com/pipermail/sdl/2002-September/049078.html
    saved_flags := SDL_Surface^.flags and (SDL_SRCALPHA or SDL_RLEACCELOK);
    saved_alpha := SDL_Surface^.format^.alpha;

    if ((saved_flags and SDL_SRCALPHA) = SDL_SRCALPHA) then SDL_SetAlpha(SDL_Surface, 0, 0);

    // Set color key
    {if (SDL_Surface^.format^.colorkey > 0) then
    begin
      SDL_GetRGB(SDL_Surface^.format^.colorkey, SDL_Surface^.format, clrk_r, clrk_g, clrk_b);

      colorkey := SDL_MapRGBA(NewSurface^.format, clrk_r^, clrk_g^, clrk_b^, 0);
      SDL_FillRect( NewSurface, 0, colorkey ) ;

      colorkey := SDL_MapRGBA(SDL_Surface^.format, clrk_r^, clrk_g^, clrk_b^, 0);
      SDL_SetColorKey( SDL_Surface, SDL_SRCCOLORKEY, colorkey ) ;
    end;}

    // Set are to be blitted
    area.x := 0;
    area.y := 0;
    area.w := SDL_Surface^.w;
    area.h := SDL_Surface^.h;

    // Blit old surface to new texture surface
    SDL_BlitSurface( SDL_Surface, @area, NewSurface, @area ) ;

    // Apply saved alpha
    if((saved_flags and SDL_SRCALPHA) = SDL_SRCALPHA) then SDL_SetAlpha(SDL_Surface, saved_flags, saved_alpha);

    // Create OpenGL texture
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    glGenTextures(1, @Texture);
    glBindTexture(GL_TEXTURE_2D, Texture);
    
    {$IFDEF USE_DGL_HEADER}
    if ((not GL_VERSION_2_0) or (not SupportsNonPowerOfTwo)) then
    {$ELSE}
    if ((not Load_GL_version_2_0) or (not SupportsNonPowerOfTwo)) then
    {$ENDIF}
    begin
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    end;
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) ;
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) ;

    if NewSurface.format.BytesPerPixel > 3 then
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, NewSurface^.w, NewSurface^.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NewSurface^.pixels)
    else
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, NewSurface^.w, NewSurface^.h, 0, GL_RGB, GL_UNSIGNED_BYTE, NewSurface^.pixels);

    // Create mipmap if necessary
    if BuildMipMap then
    begin
      if NewSurface.format.BytesPerPixel > 3 then gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, SrfWidth, SrfHeight, GL_RGBA, GL_UNSIGNED_BYTE, NewSurface^.pixels)
                                              else gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, SrfWidth, SrfHeight, GL_RGB, GL_UNSIGNED_BYTE, NewSurface^.pixels);
    end;

    // Free no longer needed surfaces
    SDL_FreeSurface(NewSurface);
    SDL_FreeSurface(SDL_Surface);
    Result := Texture;
  end;
End;

function LoadTexture(Filename: String; var Texture: GLuint): Boolean;
begin
  Result := false;
  if FileExists(ExtractFilePath(ParamStr(0)) + Filename) then
  begin
    {$IFDEF USE_SDL_IMAGE}
	Texture := CreateTexture(IMG_Load(PChar(ExtractFilePath(ParamStr(0)) + Filename)));
    {$ENDIF}
	{$IFDEF USE_VAMPYRE}
	Texture := CreateTexture(LoadSDLSurfaceFromFile(ExtractFilePath(ParamStr(0)) + Filename));
	{$ENDIF}
	Result := true;
  end;
end;

function LoadTexture(Surface: PSDL_Surface; var Texture: GLuint): Boolean;
begin
  Result := false;
  if Surface <> nil then
  begin
    Texture := CreateTexture(Surface);
    Result := true;
  end;
end;

procedure SwapBGR(SDL_Surface: PSDL_Surface);
var
  x,y: Integer;
  pixel: UInt32;
  pred, pgreen, pblue, palpha: PUInt8;
  red, green, blue, alpha: UInt8;
begin
  for y:=0 to (SDL_Surface.h-1) do
    for x:=0 to (SDL_Surface.w-1) do
    begin
      //reads the pixels them
      pixel:=SDL_GetPixel(SDL_Surface, x, y);

      pred:=@red;
      pgreen:=@green;
      pblue:=@blue;
      palpha:=@alpha;

      //read and swap color
      SDL_GetRGBA(pixel, SDL_Surface.format, pred, pgreen, pblue, palpha);
      pixel:=SDL_MapRGBA(SDL_Surface.format, blue, green, red, alpha);

      SDL_PutPixel(SDL_Surface, x, y, pixel);
    end;
end;



procedure Bind(var Texture: GLuint);
begin
  glBindTexture(GL_TEXTURE_2D, Texture);
end;

end.
