DEADSOFTWARE

panels: handle panel animation in render (bump protocol)
[d2df-sdl.git] / src / game / renders / opengl / r_textures.pas
index d24c2ac6708ce570384f7335d34513ae3ee49eaa..062f76bdcebe373f1ecc231c88ba127b7724d476 100644 (file)
@@ -23,8 +23,9 @@ interface
     {$ELSE}
       GL, GLEXT,
     {$ENDIF}
-    r_atlas,
-    utils    // SSArray
+    g_base, g_animations,  // TRectHW, TAnimInfo
+    utils,
+    r_atlas, r_fonts
   ;
 
   type
@@ -97,12 +98,41 @@ interface
         property backAnim: Boolean read mBackanim; (* this property must be located at TAnimState? *)
     end;
 
+    TGLTextureArray = array of TGLTexture;
+
+    TRectArray = array of TRectWH;
+
+    TGLFont = class sealed (TFont)
+      private
+        info: TFontInfo;
+        ch: TGLTextureArray;
+
+      public
+        destructor Destroy; override;
+        function GetChar (c: AnsiChar): TGLTexture;
+        function GetWidth (c: AnsiChar): Integer;
+        function GetMaxWidth (): Integer;
+        function GetMaxHeight (): Integer;
+        function GetSpace (): Integer;
+    end;
+
+    TAnimTextInfo = record
+      name: AnsiString;
+      w, h: Integer;
+      anim: TAnimInfo;
+    end;
+
   procedure r_Textures_Initialize;
   procedure r_Textures_Finalize;
 
   function r_Textures_LoadFromFile (const filename: AnsiString; log: Boolean = True): TGLTexture;
   function r_Textures_LoadMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture;
   function r_Textures_LoadMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; backanim: Boolean; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; log: Boolean = True): TGLMultiTexture;
+
+  function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean;
+
+  function r_Textures_LoadFontFromFile (const filename: AnsiString; constref f: TFontInfo; skipch: Integer; log: Boolean = true): TGLFont;
 
 implementation
 
@@ -115,7 +145,6 @@ implementation
   var
     maxTileSize: Integer;
     atl: array of TGLAtlas;
-//    tex: array of TGLTexture;
 
   (* --------- TGLAtlasNode --------- *)
 
@@ -360,10 +389,17 @@ implementation
     end;
   end;
 
+  function r_Textures_FixImageData (var img: TImageData): Boolean;
+  begin
+    result := false;
+    if ConvertImage(img, TImageFormat.ifA8R8G8B8) then
+      if SwapChannels(img, ChannelRed, ChannelBlue) then // wtf
+        result := true;
+  end;
+
   function r_Textures_LoadFromImage (var img: TImageData): TGLTexture;
     var t: TGLTexture; n: TGLAtlasNode; c: TDynImageDataArray; cw, ch, i, j: LongInt;
   begin
-    // e_logwritefln('r_Textures_CreateFromImage: w=%s h=%s', [img.width, img.height]);
     result := nil;
     if SplitImage(img, c, maxTileSize, maxTileSize, cw, ch, False) then
     begin
@@ -396,9 +432,8 @@ implementation
       InitImage(img);
       try
         if LoadImageFromMemory(data, size, img) then
-          if ConvertImage(img, TImageFormat.ifA8R8G8B8) then
-            if SwapChannels(img, ChannelRed, ChannelBlue) then // wth
-              result := r_Textures_LoadFromImage(img)
+          if r_Textures_FixImageData(img) then
+            result := r_Textures_LoadFromImage(img)
       except
       end;
       FreeImage(img);
@@ -459,53 +494,65 @@ implementation
       InitImage(img);
       try
         if LoadImageFromMemory(data, size, img) then
-          if ConvertImage(img, TImageFormat.ifA8R8G8B8) then
-            if SwapChannels(img, ChannelRed, ChannelBlue) then // wtf
-              result := r_Textures_LoadMultiFromImageAndInfo(img, w, h, c, b)
+          if r_Textures_FixImageData(img) then
+            result := r_Textures_LoadMultiFromImageAndInfo(img, w, h, c, b)
       except
       end;
       FreeImage(img);
     end;
   end;
 
-  function r_Textures_LoadMultiFromWad (wad: TWADFile): TGLMultiTexture;
-    var data: Pointer; size: LongInt; TexRes: AnsiString; w, h, c: Integer; b: Boolean; cfg: TConfig; img: TImageData;
+  function r_Textures_LoadTextFromMemory (data: Pointer; size: LongInt; var txt: TAnimTextInfo): Boolean;
+    var cfg: TConfig;
+  begin
+    result := false;
+    if data <> nil then
+    begin
+      cfg := TConfig.CreateMem(data, size);
+      if cfg <> nil then
+      begin
+        txt.name := cfg.ReadStr('', 'resource', '');
+        txt.w := MAX(0, cfg.ReadInt('', 'framewidth', 0));
+        txt.h := MAX(0, cfg.ReadInt('', 'frameheight', 0));
+        txt.anim.loop := true;
+        txt.anim.delay := MAX(0, cfg.ReadInt('', 'waitcount', 0));
+        txt.anim.frames := MAX(0, cfg.ReadInt('', 'framecount', 0));
+        txt.anim.back := cfg.ReadBool('', 'backanim', false);
+        cfg.Free;
+        result := (txt.name <> '') and (txt.w > 0) and (txt.h > 0) and (txt.anim.delay > 0) and (txt.anim.frames > 0);
+      end;
+    end;
+  end;
+
+  function r_Textures_LoadMultiFromWad (wad: TWADFile; var txt: TAnimTextInfo): TGLMultiTexture;
+    var data: Pointer; size: LongInt; img: TImageData;
   begin
     ASSERT(wad <> nil);
     result := nil;
     if wad.GetResource('TEXT/ANIM', data, size) then
     begin
-      cfg := TConfig.CreateMem(data, size);
-      FreeMem(data);
-      if cfg <> nil then
+      if r_Textures_LoadTextFromMemory(data, size, txt) then
       begin
-        TexRes := cfg.ReadStr('', 'resource', '');
-        w := cfg.ReadInt('', 'framewidth', 0);
-        h := cfg.ReadInt('', 'frameheight', 0);
-        c := cfg.ReadInt('', 'framecount', 0);
-        b := cfg.ReadBool('', 'backanim', false);
-        if (TexRes <> '') and (w > 0) and (h > 0) and (c > 0) then
+        FreeMem(data);
+        if wad.GetResource('TEXTURES/' + txt.name, data, size) then
         begin
-          if wad.GetResource('TEXTURES/' + TexRes, data, size) then
-          begin
-            InitImage(img);
-            try
-              if LoadImageFromMemory(data, size, img) then
-                if ConvertImage(img, TImageFormat.ifA8R8G8B8) then
-                  if SwapChannels(img, ChannelRed, ChannelBlue) then // wtf
-                    result := r_Textures_LoadMultiFromImageAndInfo(img, w, h, c, b)
-            finally
-              FreeMem(data);
-            end;
-            FreeImage(img);
-          end
+          InitImage(img);
+          try
+            if LoadImageFromMemory(data, size, img) then
+              if r_Textures_FixImageData(img) then
+                result := r_Textures_LoadMultiFromImageAndInfo(img, txt.w, txt.h, txt.anim.frames, txt.anim.back);
+          finally
+            FreeMem(data);
+          end;
+          FreeImage(img);
         end;
-        cfg.Free;
       end
+      else
+        FreeMem(data);
     end;
   end;
 
-  function r_Textures_LoadMultiFromMemory (data: Pointer; size: LongInt): TGLMultiTexture;
+  function r_Textures_LoadMultiFromMemory (data: Pointer; size: LongInt; var txt: TAnimTextInfo): TGLMultiTexture;
     var wad: TWADFile; t: TGLTexture; m: TGLMultiTexture;
   begin
     result := nil;
@@ -518,6 +565,13 @@ implementation
         SetLength(m.mTexture, 1);
         m.mTexture[0] := t;
         m.mBackanim := false;
+        txt.name := '';
+        txt.w := m.width;
+        txt.h := m.height;
+        txt.anim.loop := true;
+        txt.anim.delay := 1;
+        txt.anim.frames := 1;
+        txt.anim.back := false;
         result := m;
       end
       else if IsWadData(data, size) then
@@ -525,15 +579,15 @@ implementation
         wad := TWADFile.Create();
         if wad.ReadMemory(data, size) then
         begin
-          result := r_Textures_LoadMultiFromWad(wad);
+          result := r_Textures_LoadMultiFromWad(wad, txt);
           wad.Free;
         end
       end
     end
   end;
 
-  function r_Textures_LoadMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture;
-    var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer; t: TGLTexture;
+  function r_Textures_LoadMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; log: Boolean = True): TGLMultiTexture;
+    var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
   begin
     result := nil;
     wadName := g_ExtractWadName(filename);
@@ -543,13 +597,19 @@ implementation
       resName := g_ExtractFilePathName(filename);
       if wad.GetResource(resName, data, size, log) then
       begin
-        result := r_Textures_LoadMultiFromMemory(data, size);
+        result := r_Textures_LoadMultiFromMemory(data, size, txt);
         FreeMem(data);
       end;
       wad.Free
     end
   end;
 
+  function r_Textures_LoadMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture;
+    var txt: TAnimTextInfo;
+  begin
+    result := r_Textures_LoadMultiTextFromFile(filename, txt, log);
+  end;
+
   function r_Textures_LoadMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; backanim: Boolean; log: Boolean = True): TGLMultiTexture;
     var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
   begin
@@ -571,4 +631,208 @@ implementation
     end
   end;
 
+  function r_Textures_GetRect (var img: TImageData): TRectWH;
+    var i, j, w, h: Integer; done: Boolean;
+
+    function IsVoid (i, j: Integer): Boolean; inline;
+    begin
+      result := GetPixel32(img, i, j).Channels[3] = 0
+    end;
+
+  begin
+    w := img.Width;
+    h := img.Height;
+
+    (* trace x from right to left *)
+    done := false; i := 0;
+    while not done and (i < w) do
+    begin
+      j := 0;
+      while (j < h) and IsVoid(i, j) do inc(j);
+      done := (j < h) and (IsVoid(i, j) = false);
+      result.x := i;
+      inc(i);
+    end;
+
+    (* trace y from up to down *)
+    done := false; j := 0;
+    while not done and (j < h) do
+    begin
+      i := 0;
+      while (i < w) and IsVoid(i, j) do inc(i);
+      done := (i < w) and (IsVoid(i, j) = false);
+      result.y := j;
+      inc(j);
+    end;
+
+    (* trace x from right to left *)
+    done := false; i := w - 1;
+    while not done and (i >= 0) do
+    begin
+      j := 0;
+      while (j < h) and IsVoid(i, j) do inc(j);
+      done := (j < h) and (IsVoid(i, j) = false);
+      result.width := i - result.x + 1;
+      dec(i);
+    end;
+
+    (* trace y from down to up *)
+    done := false; j := h - 1;
+    while not done and (j >= 0) do
+    begin
+      i := 0;
+      while (i < w) and IsVoid(i, j) do inc(i);
+      done := (i < w) and (IsVoid(i, j) = false);
+      result.height := j - result.y + 1;
+      dec(j);
+    end;
+  end;
+
+  function r_Textures_LoadStreamFromImage (var img: TImageData; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray): Boolean;
+    var i, x, y: Integer; t: TImageData;
+  begin
+    ASSERT(w >= 0);
+    ASSERT(h >= 0);
+    ASSERT(c >= 1);
+    ASSERT(cw >= 1);
+    ASSERT((st <> nil) and (Length(st) >= c));
+    ASSERT((rs = nil) or (Length(rs) >= c));
+    result := true;
+    for i := 0 to c - 1 do
+    begin
+      x := i mod cw;
+      y := i div cw;
+      InitImage(t);
+      st[i] := nil;
+      if NewImage(w, h, img.Format, t) then
+      begin
+        if CopyRect(img, x * w, y * h, w, h, t, 0, 0) then
+        begin
+          if rs <> nil then
+            rs[i] := r_Textures_GetRect(t);
+          st[i] := r_Textures_LoadFromImage(t);
+        end;
+      end;
+      ASSERT(st[i] <> nil);
+      FreeImage(t);
+    end;
+  end;
+
+  function r_Textures_LoadStreamFromMemory (data: Pointer; size: LongInt; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray): Boolean;
+    var img: TImageData;
+  begin
+    ASSERT(w >= 0);
+    ASSERT(h >= 0);
+    ASSERT(c >= 1);
+    ASSERT(cw >= 1);
+    ASSERT((st <> nil) and (Length(st) >= c));
+    ASSERT((rs = nil) or (Length(rs) >= c));
+    result := false;
+    if (data <> nil) and (size > 0) then
+    begin
+      InitImage(img);
+      try
+        if LoadImageFromMemory(data, size, img) then
+        begin
+          if r_Textures_FixImageData(img) then
+          begin
+            result := r_Textures_LoadStreamFromImage(img, w, h, c, cw, st, rs)
+          end;
+        end;
+      except
+      end;
+      FreeImage(img);
+    end;
+  end;
+
+  function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean;
+    var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
+  begin
+    ASSERT(w > 0);
+    ASSERT(h > 0);
+    ASSERT(count >= 1);
+    ASSERT(cw >= 1);
+    ASSERT((st <> nil) and (Length(st) >= count));
+    ASSERT((rs = nil) or (Length(rs) >= count));
+    result := false;
+    wadName := g_ExtractWadName(filename);
+    wad := TWADFile.Create();
+    if wad.ReadFile(wadName) then
+    begin
+      resName := g_ExtractFilePathName(filename);
+      if wad.GetResource(resName, data, size, log) then
+      begin
+        result := r_Textures_LoadStreamFromMemory(data, size, w, h, count, cw, st, rs);
+        FreeMem(data);
+      end;
+      wad.Free
+    end;
+  end;
+
+  (* --------- TGLFont --------- *)
+
+  function r_Textures_LoadFontFromFile (const filename: AnsiString; constref f: TFontInfo; skipch: Integer; log: Boolean = true): TGLFont;
+    var i: Integer; st: TGLTextureArray; font: TGLFont; t: TGLTexture;
+  begin
+    ASSERT(skipch >= 0);
+    result := nil;
+    SetLength(st, 256);
+    if r_Textures_LoadStreamFromFile(filename, f.w, f.h, 256, 16, st, nil, log) then
+    begin
+      if skipch > 0 then
+      begin
+        for i := 0 to 255 do
+        begin
+          t := st[i];
+          st[i] := st[(i + skipch) mod 256];
+          st[(i + skipch) mod 256] := t;
+        end;
+      end;
+      font := TGLFont.Create();
+      font.info := f;
+      font.ch := st;
+      result := font;
+    end;
+  end;
+
+  destructor TGLFont.Destroy;
+    var i: Integer;
+  begin
+    if self.ch <> nil then
+      for i := 0 to High(self.ch) do
+        self.ch[i].Free;
+    self.ch := nil;
+  end;
+
+  function TGLFont.GetChar (c: AnsiChar): TGLTexture;
+  begin
+    result := self.ch[ORD(c)];
+  end;
+
+  function TGLFont.GetWidth (c: AnsiChar): Integer;
+  begin
+    result := self.info.ch[c].w;
+    if result = 0 then
+      result := self.info.w;
+    if self.info.kern < 0 then
+      result := result + self.info.kern;
+  end;
+
+  function TGLFont.GetMaxWidth (): Integer;
+  begin
+    result := self.info.w;
+    if self.info.kern < 0 then
+      result := result + self.info.kern;
+  end;
+
+  function TGLFont.GetMaxHeight (): Integer;
+  begin
+    result := self.info.h;
+  end;
+
+  function TGLFont.GetSpace (): Integer;
+  begin
+    result := self.info.kern;
+  end;
+
 end.