diff --git a/examples/simple/TextSuiteTest.lpi b/examples/simple/TextSuiteTest.lpi
index aae3bfa..0c018cf 100644
--- a/examples/simple/TextSuiteTest.lpi
+++ b/examples/simple/TextSuiteTest.lpi
@@ -33,7 +33,7 @@
-
+
@@ -97,6 +97,11 @@
+
+
+
+
+
diff --git a/examples/simple/TextSuiteTest.lpr b/examples/simple/TextSuiteTest.lpr
index cdfe0a4..91b999c 100644
--- a/examples/simple/TextSuiteTest.lpr
+++ b/examples/simple/TextSuiteTest.lpr
@@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, sysutils, Forms, uMainForm, utsFontCreatorGDI, utsUtils, utsTypes, utsTtfUtils, utsTextSuite,
- utsRendererOpenGL, utsCodePages, utsPostProcess, utsFontCreatorFreeType, utsGDI, utsFreeType;
+ utsRendererOpenGL, utsCodePages, utsPostProcess, utsFontCreatorFreeType, utsGDI, utsFreeType, utsOpenGLUtils;
{$R *.res}
diff --git a/examples/simple/TextSuiteTest.lps b/examples/simple/TextSuiteTest.lps
index 7ebd904..0a19193 100644
--- a/examples/simple/TextSuiteTest.lps
+++ b/examples/simple/TextSuiteTest.lps
@@ -4,13 +4,13 @@
-
+
-
+
@@ -19,48 +19,45 @@
-
+
-
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
-
+
-
+
-
-
+
@@ -68,18 +65,18 @@
-
+
+
-
-
+
@@ -87,7 +84,7 @@
-
+
@@ -95,39 +92,36 @@
-
+
-
+
-
-
+
-
+
-
-
+
-
+
-
-
+
@@ -255,27 +249,24 @@
-
+
-
-
+
-
-
+
-
@@ -321,19 +312,17 @@
-
+
-
-
+
-
@@ -374,9 +363,8 @@
-
+
-
@@ -436,12 +424,11 @@
-
+
-
@@ -506,133 +493,142 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
diff --git a/utsFreeType.pas b/utsFreeType.pas
index 2054d0a..040ed21 100644
--- a/utsFreeType.pas
+++ b/utsFreeType.pas
@@ -610,7 +610,7 @@ const
{$ELSEIF DEFINED(TS_FT_WIN64)}
LIB_FREE_TYPE = 'freetype6-x64.dll';
{$ELSEIF DEFINED(TS_FT_LINUX)}
- LIB_FREE_TYPE = ???
+ LIB_FREE_TYPE = 'libfreetype.so';
{$ELSE}
{$ERROR 'unknown/unsupported OS'}
{$IFEND}
diff --git a/utsOpenGLUtils.pas b/utsOpenGLUtils.pas
new file mode 100644
index 0000000..56cf9c3
--- /dev/null
+++ b/utsOpenGLUtils.pas
@@ -0,0 +1,387 @@
+unit utsOpenGLUtils;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, syncobjs,
+ utsTextSuite, utsTypes;
+
+type
+ TtsQuadPosF = array[0..3] of TtsPositionF;
+ TtsCharRenderRefOpenGL = class(TtsCharRenderRef)
+ public
+ TextureID: Integer; // ID of OpenGL texture where the char is stored in
+ TexCoordSize: TtsPositionF; // size of the char in texture coords (0.0 - 1.0)
+ TexCoordPos: TtsPositionF; // position of the char in texture coords (0.0 - 1.0)
+ VertexSize: TtsPositionF; // size of the char in world coords
+ VertexPos: TtsPositionF; // size of the char in world coords
+
+ constructor Create;
+ end;
+
+ PtsTextureUsageItem = ^TtsTextureUsageItem;
+ TtsTextureUsageItem = packed record
+ children: array[0..3] of PtsTextureUsageItem;
+ end;
+
+ PtsTextureTreeItem = ^TtsTextureTreeItem;
+ TtsTextureTreeItem = packed record
+ value: SmallInt;
+ children: array[0..1] of PtsTextureTreeItem;
+ ref: TtsCharRenderRefOpenGL;
+ end;
+
+ PtsFontTexture = ^TtsFontTexture;
+ TtsFontTexture = packed record
+ ID: Integer; // OpenGL texture ID
+ Usage: PtsTextureTreeItem ; // tree of used texture space
+ Next: PtsFontTexture; // next texture in list
+ Prev: PtsFontTexture; // previouse texture in list
+ Size: Integer; // size of this texture
+ Count: Integer; // number of chars stored in this texture
+ end;
+
+ TtsBaseOpenGL = class(TtsRenderer)
+ private
+ fTextureSize: Integer;
+ fColor: TtsColor4f;
+ fRenderPos: TtsPosition;
+ fFirstTexture: PtsFontTexture;
+ fLastTexture: PtsFontTexture;
+
+ procedure FreeTextures(var aTexture: PtsFontTexture);
+ procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
+ protected
+ property Color: TtsColor4f read fColor;
+ property RenderPos: TtsPosition read fRenderPos;
+
+ procedure PushTexture(const aTexture: PtsFontTexture);
+
+ function CreateNewTexture: PtsFontTexture; virtual;
+ procedure FreeTexture(var aTexture: PtsFontTexture); virtual;
+ procedure UploadTexData(const aCharRef: TtsCharRenderRefOpenGL;
+ const aCharImage: TtsImage; const X, Y: Integer); virtual;
+
+ protected
+ function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; override;
+ procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); override;
+
+ procedure BeginRender; override;
+
+ procedure SetDrawPos(const X, Y: Integer); override;
+ function GetDrawPos: TtsPosition; override;
+ procedure MoveDrawPos(const X, Y: Integer); override;
+ procedure SetColor(const aColor: TtsColor4f); override;
+ public
+ property TextureSize: Integer read fTextureSize write fTextureSize;
+
+ constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
+ destructor Destroy; override;
+ end;
+
+ EtsRendererOpenGL = class(EtsRenderer);
+
+implementation
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsCharRenderRefOpenGL////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsCharRenderRefOpenGL.Create;
+begin
+ inherited Create;
+ TextureID := 0;
+ FillByte(TexCoordPos, SizeOf(TexCoordPos), 0);
+ FillByte(TexCoordSize, SizeOf(TexCoordSize), 0);
+ FillByte(VertexPos, SizeOf(VertexPos), 0);
+ FillByte(VertexSize, SizeOf(VertexSize), 0);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//TtsBaseOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.FreeTextures(var aTexture: PtsFontTexture);
+begin
+ if not Assigned(aTexture) then
+ exit;
+ FreeTextures(aTexture^.Next);
+ FreeTexture(aTexture);
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
+begin
+ if not Assigned(aItem) then
+ exit;
+ FreeTextureTreeItem(aItem^.children[0]);
+ FreeTextureTreeItem(aItem^.children[1]);
+ Dispose(aItem);
+ aItem := nil;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.PushTexture(const aTexture: PtsFontTexture);
+begin
+ aTexture^.Prev := fLastTexture;
+ if Assigned(fLastTexture) then
+ fLastTexture^.Next := aTexture
+ else
+ fFirstTexture := aTexture;
+ fLastTexture := aTexture;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function TtsBaseOpenGL.CreateNewTexture: PtsFontTexture;
+begin
+ result := nil;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.FreeTexture(var aTexture: PtsFontTexture);
+begin
+ if not Assigned(aTexture) then
+ exit;
+ FreeTextureTreeItem(aTexture^.Usage);
+ if Assigned(aTexture^.Prev) then
+ aTexture^.Prev^.Next := aTexture^.Next;
+ if Assigned(aTexture^.Next) then
+ aTexture^.Next^.Prev := aTexture^.Prev;
+ Dispose(aTexture);
+ aTexture := nil;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.UploadTexData(const aCharRef: TtsCharRenderRefOpenGL;
+ const aCharImage: TtsImage; const X, Y: Integer);
+begin
+ // DUMMY
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef;
+var
+ GlyphWidth, GlyphHeight: Integer;
+
+ function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
+ var
+ w, h: Integer;
+ begin
+ result := nil;
+ w := X2 - X1;
+ h := Y2 - Y1;
+ if not Assigned(aItem) or
+ Assigned(aItem^.ref) or
+ (w < GlyphWidth) or
+ (h < GlyphHeight) then
+ exit;
+
+ if (aItem^.value > 0) then begin
+ result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
+ if not Assigned(result) then
+ result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
+ end else if (aItem^.value < 0) then begin
+ result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
+ if not Assigned(result) then
+ result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
+ end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
+ X := X1;
+ Y := Y1;
+ result := aItem;
+ end else begin
+ new(aItem^.children[0]);
+ new(aItem^.children[1]);
+ FillByte(aItem^.children[0]^, SizeOf(aItem^.children[0]^), 0);
+ FillByte(aItem^.children[1]^, SizeOf(aItem^.children[1]^), 0);
+ if (w - GlyphWidth) < (h - GlyphHeight) then begin
+ aItem^.value := Y1 + GlyphHeight;
+ result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
+ end else begin
+ aItem^.value := -(X1 + GlyphWidth);
+ result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
+ end;
+ end;
+ end;
+
+ function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL;
+ var
+ x, y: Integer;
+ item: PtsTextureTreeItem;
+ begin
+ item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
+ if not Assigned(item) then
+ raise EtsRendererOpenGL.Create('unable to add glyph to texture');
+ item^.ref := TtsCharRenderRefOpenGL.Create;
+ result := item^.ref;
+
+ // Text Coords
+ result.TextureID := aTexture^.ID;
+ result.TexCoordPos.x := x / aTexture^.Size;
+ result.TexCoordPos.y := y / aTexture^.Size;
+ result.TexCoordSize.x := aCharImage.Width / aTexture^.Size;
+ result.TexCoordSize.y := aCharImage.Height / aTexture^.Size;
+
+ // Vertex Coords
+ result.VertexPos.x := -aChar.GlyphRect.Left;
+ result.VertexPos.y := -aChar.GlyphRect.Top - aChar.GlyphOrigin.y;
+ result.VertexSize.x := aCharImage.Width;
+ result.VertexSize.y := aCharImage.Height;
+
+ UploadTexData(result, aCharImage, x, y);
+ end;
+
+var
+ tex: PtsFontTexture;
+begin
+ result := nil;
+ if aCharImage.IsEmpty then
+ exit;
+
+ GlyphWidth := aCharImage.Width + 1;
+ GlyphHeight := aCharImage.Height + 1;
+
+ // try to add to existing texture
+ tex := fFirstTexture;
+ while Assigned(tex) and not Assigned(result) do begin
+ result := AddToTexture(tex);
+ tex := tex^.Next;
+ end;
+
+ // create new texture
+ if not Assigned(result) then begin
+ if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then
+ raise EtsRendererOpenGL.Create('char is to large to fit into a texture: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
+ tex := CreateNewTexture;
+ result := AddToTexture(tex);
+ end;
+
+ if not Assigned(result) then
+ raise EtsRendererOpenGL.Create('unable to creat render reference for char: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef);
+var
+ ref: TtsCharRenderRefOpenGL;
+ tex: PtsFontTexture;
+
+ function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
+ begin
+ result :=
+ Assigned(aItem) and
+ not Assigned(aItem^.children[0]) and
+ not Assigned(aItem^.children[1]) and
+ not Assigned(aItem^.ref);
+ end;
+
+ function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
+ var
+ w, h: Integer;
+ begin
+ w := X2 - X1;
+ h := Y2 - Y1;
+ if not Assigned(aItem) or
+ (w < ref.VertexSize.x) or
+ (h < ref.VertexSize.y) then
+ exit;
+
+ result := (aItem^.ref = ref);
+ if not result then begin
+ if (aItem^.value > 0) then begin
+ result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
+ result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
+ end else if (aItem^.value < 0) then begin
+ result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
+ result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
+ end;
+ end else
+ aItem^.ref := nil;
+
+ if result and
+ IsEmtpy(aItem^.children[0]) and
+ IsEmtpy(aItem^.children[1]) then
+ begin
+ FreeTextureTreeItem(aItem^.children[0]);
+ FreeTextureTreeItem(aItem^.children[1]);
+ FillByte(aItem^, SizeOf(aItem^), 0);
+ end;
+ end;
+
+begin
+ try
+ if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then
+ exit;
+ ref := (aCharRef as TtsCharRenderRefOpenGL);
+ tex := fFirstTexture;
+ while Assigned(tex) do begin
+ if (tex^.ID = ref.TextureID) then begin
+ if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
+ raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
+ if IsEmtpy(tex^.Usage) then begin
+ if (tex = fFirstTexture) then
+ fFirstTexture := nil;
+ FreeTexture(tex);
+ end;
+ tex := nil;
+ end else
+ tex := tex^.Next;
+ end;
+ finally
+ if Assigned(aCharRef) then
+ aCharRef.Free;
+ end;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.BeginRender;
+begin
+ inherited BeginRender;
+ fRenderPos.x := 0;
+ fRenderPos.y := 0;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.SetDrawPos(const X, Y: Integer);
+begin
+ fRenderPos.x := X;
+ fRenderPos.y := Y;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function TtsBaseOpenGL.GetDrawPos: TtsPosition;
+begin
+ result := fRenderPos;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.MoveDrawPos(const X, Y: Integer);
+begin
+ fRenderPos.x := fRenderPos.x + X;
+ fRenderPos.y := fRenderPos.y + Y;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TtsBaseOpenGL.SetColor(const aColor: TtsColor4f);
+begin
+ fColor := aColor;
+end;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+constructor TtsBaseOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
+begin
+ inherited Create(aContext, aFormat);
+ fFirstTexture := nil;
+ fLastTexture := nil;
+ fTextureSize := 2048;
+ fColor := tsColor4f(1, 1, 1, 1);
+ fRenderPos := tsPosition(0, 0);
+end;
+
+destructor TtsBaseOpenGL.Destroy;
+begin
+ FreeTextures(fFirstTexture);
+ inherited Destroy;
+end;
+
+end.
+
diff --git a/utsRendererOpenGL.pas b/utsRendererOpenGL.pas
index 0b7172c..8ac0d31 100644
--- a/utsRendererOpenGL.pas
+++ b/utsRendererOpenGL.pas
@@ -5,79 +5,32 @@ unit utsRendererOpenGL;
interface
uses
- Classes, SysUtils, syncobjs, dglOpenGL,
- utsTextSuite, utsTypes;
+ Classes, SysUtils,
+ utsTextSuite, utsTypes, utsOpenGLUtils, dglOpenGL;
type
- TtsQuadPosF = array[0..3] of TtsPositionF;
- TtsCharRenderRefOpenGL = class(TtsCharRenderRef)
- private
- TextureID: GLint; // ID of OpenGL texture where the char is stored in
- TexCoordSize: TtsPositionF; // size of the char in texture coords (0.0 - 1.0)
- TexCoordPos: TtsPositionF; // position of the char in texture coords (0.0 - 1.0)
- VertexSize: TtsPositionF; // size of the char in world coords
- VertexPos: TtsPositionF; // size of the char in world coords
- public
- constructor Create;
- end;
-
- PtsTextureUsageItem = ^TtsTextureUsageItem;
- TtsTextureUsageItem = packed record
- children: array[0..3] of PtsTextureUsageItem;
- end;
-
- PtsTextureTreeItem = ^TtsTextureTreeItem;
- TtsTextureTreeItem = packed record
- value: SmallInt;
- children: array[0..1] of PtsTextureTreeItem;
- ref: TtsCharRenderRefOpenGL;
- end;
-
- PtsFontTexture = ^TtsFontTexture;
- TtsFontTexture = packed record
- ID: GLint; // OpenGL texture ID
- Usage: PtsTextureTreeItem ; // tree of used texture space
- Next: PtsFontTexture; // next texture in list
- Prev: PtsFontTexture; // previouse texture in list
- Size: Integer; // size of this texture
- Count: Integer; // number of chars stored in this texture
- end;
-
- TtsRendererOpenGL = class(TtsRenderer)
+ TtsRendererOpenGL = class(TtsBaseOpenGL)
private
fVBO: GLuint;
- fTextureSize: Integer;
- fColor: TtsColor4f;
- fRenderPos: TtsPosition;
fIsRendering: Boolean;
- fFirstTexture: PtsFontTexture;
- fLastTexture: PtsFontTexture;
-
- function CreateNewTexture: PtsFontTexture;
- procedure FreeTexture(var aTexture: PtsFontTexture);
- procedure FreeTextures(var aTexture: PtsFontTexture);
- procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
protected
- function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; override;
- procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); override;
+ function CreateNewTexture: PtsFontTexture; override;
+ procedure FreeTexture(var aTexture: PtsFontTexture); override;
+ procedure UploadTexData(const aCharRef: TtsCharRenderRefOpenGL;
+ const aCharImage: TtsImage; const X, Y: Integer); override;
procedure BeginRender; override;
procedure EndRender; override;
procedure SetDrawPos(const X, Y: Integer); override;
- function GetDrawPos: TtsPosition; override;
procedure MoveDrawPos(const X, Y: Integer); override;
procedure SetColor(const aColor: TtsColor4f); override;
procedure Render(const aCharRef: TtsCharRenderRef); override;
public
- property TextureSize: Integer read fTextureSize write fTextureSize;
-
constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
destructor Destroy; override;
end;
- EtsRendererOpenGL = class(EtsRenderer);
-
implementation
type
@@ -117,23 +70,10 @@ const
VBO_DATA: array[0..3] of TVertex = (
(pos: (0.0, 0.0); tex: (0.0, 0.0)),
(pos: (0.0, 1.0); tex: (0.0, 1.0)),
- (pos: (1.0, 1.0); tex: (1.0, 1.0)),
- (pos: (1.0, 0.0); tex: (1.0, 0.0))
+ (pos: (1.0, 0.0); tex: (1.0, 0.0)),
+ (pos: (1.0, 1.0); tex: (1.0, 1.0))
);
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//TtsCharRenderRefOpenGL////////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-constructor TtsCharRenderRefOpenGL.Create;
-begin
- inherited Create;
- TextureID := 0;
- FillByte(TexCoordPos, SizeOf(TexCoordPos), 0);
- FillByte(TexCoordSize, SizeOf(TexCoordSize), 0);
- FillByte(VertexPos, SizeOf(VertexPos), 0);
- FillByte(VertexSize, SizeOf(VertexSize), 0);
-end;
-
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TtsRendererOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -160,12 +100,7 @@ begin
FORMAT_TYPES[Format].DataFormat,
nil);
- result^.Prev := fLastTexture;
- if Assigned(fLastTexture) then
- fLastTexture^.Next := result
- else
- fFirstTexture := result;
- fLastTexture := result;
+ PushTexture(result);
except
if Assigned(result^.Usage) then
Dispose(result^.Usage);
@@ -176,216 +111,21 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TtsRendererOpenGL.FreeTexture(var aTexture: PtsFontTexture);
begin
- if not Assigned(aTexture) then
- exit;
- glDeleteTextures(1, @aTexture^.ID);
- FreeTextureTreeItem(aTexture^.Usage);
- if Assigned(aTexture^.Prev) then
- aTexture^.Prev^.Next := aTexture^.Next;
- if Assigned(aTexture^.Next) then
- aTexture^.Next^.Prev := aTexture^.Prev;
- Dispose(aTexture);
- aTexture := nil;
-end;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TtsRendererOpenGL.FreeTextures(var aTexture: PtsFontTexture);
-begin
- if not Assigned(aTexture) then
- exit;
- FreeTextures(aTexture^.Next);
- FreeTexture(aTexture);
-end;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TtsRendererOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
-begin
- if not Assigned(aItem) then
- exit;
- FreeTextureTreeItem(aItem^.children[0]);
- FreeTextureTreeItem(aItem^.children[1]);
- Dispose(aItem);
- aItem := nil;
-end;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function TtsRendererOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef;
-var
- GlyphWidth, GlyphHeight: Integer;
-
- function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
- var
- w, h: Integer;
- begin
- result := nil;
- w := X2 - X1;
- h := Y2 - Y1;
- if not Assigned(aItem) or
- Assigned(aItem^.ref) or
- (w < GlyphWidth) or
- (h < GlyphHeight) then
- exit;
-
- if (aItem^.value > 0) then begin
- result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
- if not Assigned(result) then
- result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
- end else if (aItem^.value < 0) then begin
- result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
- if not Assigned(result) then
- result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
- end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
- X := X1;
- Y := Y1;
- result := aItem;
- end else begin
- new(aItem^.children[0]);
- new(aItem^.children[1]);
- FillByte(aItem^.children[0]^, SizeOf(aItem^.children[0]^), 0);
- FillByte(aItem^.children[1]^, SizeOf(aItem^.children[1]^), 0);
- if (w - GlyphWidth) < (h - GlyphHeight) then begin
- aItem^.value := Y1 + GlyphHeight;
- result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
- end else begin
- aItem^.value := -(X1 + GlyphWidth);
- result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
- end;
- end;
- end;
-
- function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL;
- var
- x, y: Integer;
- item: PtsTextureTreeItem;
- begin
- item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
- if not Assigned(item) then
- raise EtsRendererOpenGL.Create('unable to add glyph to texture');
- item^.ref := TtsCharRenderRefOpenGL.Create;
- result := item^.ref;
-
- // Text Coords
- result.TextureID := aTexture^.ID;
- result.TexCoordPos.x := x / aTexture^.Size;
- result.TexCoordPos.y := y / aTexture^.Size;
- result.TexCoordSize.x := aCharImage.Width / aTexture^.Size;
- result.TexCoordSize.y := aCharImage.Height / aTexture^.Size;
-
- // Vertex Coords
- result.VertexPos.x := -aChar.GlyphRect.Left;
- result.VertexPos.y := -aChar.GlyphRect.Top - aChar.GlyphOrigin.y;
- result.VertexSize.x := aCharImage.Width;
- result.VertexSize.y := aCharImage.Height;
-
- glBindTexture(GL_TEXTURE_2D, result.TextureID);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- x, y, aCharImage.Width, aCharImage.Height,
- FORMAT_TYPES[aCharImage.Format].Format,
- FORMAT_TYPES[aCharImage.Format].DataFormat,
- aCharImage.Data);
- end;
-
-var
- tex: PtsFontTexture;
-begin
- result := nil;
- if aCharImage.IsEmpty then
- exit;
-
- GlyphWidth := aCharImage.Width + 1;
- GlyphHeight := aCharImage.Height + 1;
-
- // try to add to existing texture
- tex := fFirstTexture;
- while Assigned(tex) and not Assigned(result) do begin
- result := AddToTexture(tex);
- tex := tex^.Next;
- end;
-
- // create new texture
- if not Assigned(result) then begin
- if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then
- raise EtsRendererOpenGL.Create('char is to large to fit into a texture: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
- tex := CreateNewTexture;
- result := AddToTexture(tex);
- end;
-
- if not Assigned(result) then
- raise EtsRendererOpenGL.Create('unable to creat render reference for char: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
+ if Assigned(aTexture) then
+ glDeleteTextures(1, @aTexture^.ID);
+ inherited FreeTexture(aTexture);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TtsRendererOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef);
-var
- ref: TtsCharRenderRefOpenGL;
- tex: PtsFontTexture;
-
- function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
- begin
- result :=
- Assigned(aItem) and
- not Assigned(aItem^.children[0]) and
- not Assigned(aItem^.children[1]) and
- not Assigned(aItem^.ref);
- end;
-
- function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
- var
- w, h: Integer;
- begin
- w := X2 - X1;
- h := Y2 - Y1;
- if not Assigned(aItem) or
- (w < ref.VertexSize.x) or
- (h < ref.VertexSize.y) then
- exit;
-
- result := (aItem^.ref = ref);
- if not result then begin
- if (aItem^.value > 0) then begin
- result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
- result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
- end else if (aItem^.value < 0) then begin
- result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
- result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
- end;
- end else
- aItem^.ref := nil;
-
- if result and
- IsEmtpy(aItem^.children[0]) and
- IsEmtpy(aItem^.children[1]) then
- begin
- FreeTextureTreeItem(aItem^.children[0]);
- FreeTextureTreeItem(aItem^.children[1]);
- FillByte(aItem^, SizeOf(aItem^), 0);
- end;
- end;
-
+procedure TtsRendererOpenGL.UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer);
begin
- try
- if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then
- exit;
- ref := (aCharRef as TtsCharRenderRefOpenGL);
- tex := fFirstTexture;
- while Assigned(tex) do begin
- if (tex^.ID = ref.TextureID) then begin
- if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
- raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
- if IsEmtpy(tex^.Usage) then begin
- if (tex = fFirstTexture) then
- fFirstTexture := nil;
- FreeTexture(tex);
- end;
- tex := nil;
- end else
- tex := tex^.Next;
- end;
- finally
- if Assigned(aCharRef) then
- aCharRef.Free;
- end;
+ glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ x, y, aCharImage.Width, aCharImage.Height,
+ FORMAT_TYPES[aCharImage.Format].Format,
+ FORMAT_TYPES[aCharImage.Format].DataFormat,
+ aCharImage.Data);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -393,10 +133,8 @@ procedure TtsRendererOpenGL.BeginRender;
begin
inherited BeginRender;
fIsRendering := true;
- fRenderPos.x := 0;
- fRenderPos.y := 0;
glPushMatrix;
- glColor4fv(@fColor.arr[0]);
+ glColor4fv(@Color.arr[0]);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -412,32 +150,24 @@ end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TtsRendererOpenGL.SetDrawPos(const X, Y: Integer);
begin
- fRenderPos.x := X;
- fRenderPos.y := Y;
+ inherited SetDrawPos(X, Y);
glPopMatrix;
glPushMatrix;
glTranslatef(X, Y, 0);
end;
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function TtsRendererOpenGL.GetDrawPos: TtsPosition;
-begin
- result := fRenderPos;
-end;
-
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TtsRendererOpenGL.MoveDrawPos(const X, Y: Integer);
begin
- fRenderPos.x := fRenderPos.x + X;
- fRenderPos.y := fRenderPos.y + Y;
+ inherited MoveDrawPos(X, Y);
glTranslatef(X, Y, 0);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TtsRendererOpenGL.SetColor(const aColor: TtsColor4f);
begin
- fColor := aColor;
- glColor4fv(@fColor.arr[0]);
+ inherited SetColor(aColor);
+ glColor4fv(@Color.arr[0]);
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -468,7 +198,7 @@ begin
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(8));
- glDrawArrays(GL_QUADS, 0, 4);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
@@ -484,13 +214,7 @@ end;
constructor TtsRendererOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
begin
inherited Create(aContext, aFormat);
- fIsRendering := false;
- fFirstTexture := nil;
- fLastTexture := nil;
- fTextureSize := 2048;
- fColor := tsColor4f(1, 1, 1, 1);
- fRenderPos := tsPosition(0, 0);
-
+ fIsRendering := false;
glGenBuffers(1, @fVBO);
glBindBuffer(GL_ARRAY_BUFFER, fVBO);
glBufferData(GL_ARRAY_BUFFER, SizeOf(TVertex) * Length(VBO_DATA), @VBO_DATA[0].pos[0], GL_STATIC_DRAW);
@@ -500,7 +224,6 @@ end;
destructor TtsRendererOpenGL.Destroy;
begin
glDeleteBuffers(1, @fVBO);
- FreeTextures(fFirstTexture);
inherited Destroy;
end;
diff --git a/utsTextSuite.pas b/utsTextSuite.pas
index 2d4d5ab..ca68dd7 100644
--- a/utsTextSuite.pas
+++ b/utsTextSuite.pas
@@ -321,7 +321,7 @@ type
function GetRect: TtsRect;
- function PushLineItem(const aItem: PtsLineItem; const aUpdateLineWidth: Boolean = true): Boolean;
+ function PushLineItem(const aItem: PtsLineItem): Boolean;
procedure PushSpacing(const aWidth: Integer);
procedure FreeLineItem(var aItem: PtsLineItem);
procedure FreeLineItems(var aItem: PtsLineItem);
@@ -1499,7 +1499,7 @@ begin
end;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function TtsTextBlock.PushLineItem(const aItem: PtsLineItem; const aUpdateLineWidth: Boolean): Boolean;
+function TtsTextBlock.PushLineItem(const aItem: PtsLineItem): Boolean;
begin
result := false;
if not Assigned(fLastLine) then
@@ -1733,7 +1733,7 @@ begin
(fLastLine^.meta.Width + p^.TextWidth > fWidth) then
begin
if (fLastLine^.meta.Width = 0) then begin
- if not PushLineItem(p, false) then // if is first word, than add anyway
+ if not PushLineItem(p) then // if is first word, than add anyway
FreeLineItem(p);
p := nil;
end;