From 3c6da5e87cbc5f440fd9be10642b403a9d7e68f8 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Wed, 25 Feb 2015 18:52:59 +0100 Subject: [PATCH] * moved texture management of OpenGL Renderer to new class --- examples/simple/TextSuiteTest.lpi | 7 +- examples/simple/TextSuiteTest.lpr | 2 +- examples/simple/TextSuiteTest.lps | 198 ++++++++------- utsFreeType.pas | 2 +- utsOpenGLUtils.pas | 387 ++++++++++++++++++++++++++++++ utsRendererOpenGL.pas | 333 +++---------------------- utsTextSuite.pas | 6 +- 7 files changed, 523 insertions(+), 412 deletions(-) create mode 100644 utsOpenGLUtils.pas 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;