| @@ -134,5 +134,6 @@ Comments= | |||
| Count=1 | |||
| Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; | |||
| [HistoryLists\hlSearchPath] | |||
| Count=1 | |||
| Count=2 | |||
| Item0=..\utils;..\.. | |||
| Item1=..\utils;..\..;..\..\inc | |||
| @@ -5,7 +5,7 @@ interface | |||
| uses | |||
| Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, | |||
| uglcContext, | |||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorGDI, utsTypes, utsPostProcess; | |||
| utsTextSuite, utsTypes, utsUtils, utsConstants, utsPostProcessor; | |||
| type | |||
| TMainForm = class(TForm) | |||
| @@ -16,8 +16,9 @@ type | |||
| fContext: TglcContext; | |||
| ftsContext: TtsContext; | |||
| ftsRenderer: TtsRendererOpenGL; | |||
| ftsCreator1: TtsFontGeneratorGDI; | |||
| ftsCreator2: TtsFontGeneratorGDI; | |||
| ftsCreator: TtsFontCreatorGDI; | |||
| ftsPostProcessList1: TtsPostProcessorList; | |||
| ftsPostProcessList2: TtsPostProcessorList; | |||
| ftsFont1: TtsFont; | |||
| ftsFont2: TtsFont; | |||
| procedure Render; | |||
| @@ -41,7 +42,7 @@ const | |||
| procedure TMainForm.FormCreate(Sender: TObject); | |||
| var | |||
| pf: TglcContextPixelFormatSettings; | |||
| pp: TtsPostProcessStep; | |||
| pp: TtsPostProcessor; | |||
| img: TtsImage; | |||
| const | |||
| @@ -59,42 +60,50 @@ begin | |||
| ftsContext := TtsContext.Create; | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatRGBA8); | |||
| ftsCreator1 := TtsFontGeneratorGDI.Create(ftsContext); | |||
| ftsFont1 := ftsCreator1.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', ftsRenderer, 40, [], tsAANormal); | |||
| // Post processors | |||
| ftsPostProcessList1 := TtsPostProcessorList.Create(ftsContext, true); | |||
| ftsPostProcessList2 := TtsPostProcessorList.Create(ftsContext, true); | |||
| pp := TtsPostProcessFillColor.Create(tsColor4f(0, 0, 0, 1), TS_MODES_REPLACE_ALL, TS_CHANNELS_RGB); | |||
| pp.AddUsageChars(tsUsageExclude, 'Lorem'); | |||
| ftsCreator1.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorFillColor.Create(ftsContext, tsColor4f(0, 0, 0, 1), TS_IMAGE_MODES_REPLACE_ALL, TS_COLOR_CHANNELS_RGB); | |||
| pp.AddChars(tsUsageExclude, 'Lorem'); | |||
| ftsPostProcessList1.Add(pp); | |||
| pp := TtsPostProcessFillColor.Create(tsColor4f(1.0, 0.0, 0.0, 1.0), TS_MODES_MODULATE_ALL, TS_CHANNELS_RGB); | |||
| pp.AddUsageChars(tsUsageInclude, 'Lorem'); | |||
| ftsCreator1.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorFillColor.Create(ftsContext, tsColor4f(1.0, 0.0, 0.0, 1.0), TS_IMAGE_MODES_MODULATE_ALL, TS_COLOR_CHANNELS_RGB); | |||
| pp.AddChars(tsUsageInclude, 'Lorem'); | |||
| ftsPostProcessList1.Add(pp); | |||
| img := TtsImage.Create; | |||
| img.CreateEmpty(tsFormatAlpha8, 4, 4); | |||
| Move(PATTER_DATA[0], img.Data^, 16); | |||
| pp := TtsPostProcessFillPattern.Create(img, true, 0, 0, TS_MODES_MODULATE_ALL, TS_CHANNELS_RGBA); | |||
| pp.AddUsageChars(tsUsageInclude, 'Lorem'); | |||
| ftsCreator1.AddPostProcessStep(pp); | |||
| ftsCreator2 := TtsFontGeneratorGDI.Create(ftsContext); | |||
| ftsFont2 := ftsCreator2.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', ftsRenderer, 40, [tsStyleStrikeout], tsAANormal); | |||
| pp := TtsPostProcessFillColor.Create(tsColor4f(0, 0, 0.5, 1), TS_MODES_REPLACE_ALL, TS_CHANNELS_RGB); | |||
| pp.AddUsageChars(tsUsageExclude, 'e'); | |||
| ftsCreator2.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessBorder.Create(3.0, 0.5, tsColor4f(0.0, 0.5, 0.0, 1.0), true); | |||
| pp.AddUsageChars(tsUsageInclude, 'e'); | |||
| ftsCreator2.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorFillPattern.Create(ftsContext, img, true, tsPosition(0, 0), TS_IMAGE_MODES_MODULATE_ALL, TS_COLOR_CHANNELS_RGBA); | |||
| pp.AddChars(tsUsageInclude, 'Lorem'); | |||
| ftsPostProcessList2.Add(pp); | |||
| pp := TtsPostProcessorFillColor.Create(ftsContext, tsColor4f(0, 0, 0.5, 1), TS_IMAGE_MODES_REPLACE_ALL, TS_COLOR_CHANNELS_RGB); | |||
| pp.AddChars(tsUsageExclude, 'e'); | |||
| ftsPostProcessList2.Add(pp); | |||
| pp := TtsPostProcessorBorder.Create(ftsContext, 3.0, 0.5, tsColor4f(0.0, 0.5, 0.0, 1.0), true); | |||
| pp.AddChars(tsUsageInclude, 'e'); | |||
| ftsPostProcessList2.Add(pp); | |||
| // font creator and fonts | |||
| ftsCreator := TtsFontCreatorGDI.Create(ftsContext); | |||
| ftsFont1 := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', 40, [], tsAANormal); | |||
| ftsFont1.PostProcessor := ftsPostProcessList1; | |||
| ftsFont2 := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', 40, [tsStyleStrikeout], tsAANormal); | |||
| ftsFont2.PostProcessor := ftsPostProcessList2; | |||
| end; | |||
| procedure TMainForm.FormDestroy(Sender: TObject); | |||
| begin | |||
| FreeAndNil(ftsFont2); | |||
| FreeAndNil(ftsCreator2); | |||
| FreeAndNil(ftsFont1); | |||
| FreeAndNil(ftsCreator1); | |||
| FreeAndNil(ftsFont1); | |||
| FreeAndNil(ftsCreator); | |||
| FreeAndNil(ftsPostProcessList2); | |||
| FreeAndNil(ftsPostProcessList1); | |||
| FreeAndNil(ftsRenderer); | |||
| FreeAndNil(ftsContext); | |||
| FreeAndNil(fContext); | |||
| @@ -0,0 +1,4 @@ | |||
| {$DEFINE TS_ENABLE_OPENGL_SUPPORT} | |||
| {.$DEFINE TS_ENABLE_OPENGLES_SUPPORT} | |||
| {$DEFINE TS_ENABLE_GDI_SUPPORT} | |||
| {$DEFINE TS_ENABLE_FREETYPE_SUPPORT} | |||
| @@ -43,7 +43,6 @@ | |||
| <ComponentName Value="MainForm"/> | |||
| <HasResources Value="True"/> | |||
| <ResourceBaseClass Value="Form"/> | |||
| <UnitName Value="uMainForm"/> | |||
| </Unit1> | |||
| </Units> | |||
| </ProjectOptions> | |||
| @@ -54,7 +53,7 @@ | |||
| <Filename Value="PostProcess"/> | |||
| </Target> | |||
| <SearchPaths> | |||
| <IncludeFiles Value="$(ProjOutDir)"/> | |||
| <IncludeFiles Value="$(ProjOutDir);..\..\inc"/> | |||
| <OtherUnitFiles Value="..\utils;..\.."/> | |||
| <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | |||
| </SearchPaths> | |||
| @@ -7,7 +7,7 @@ interface | |||
| uses | |||
| Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | |||
| uglcContext, | |||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorGDI, utsTypes, utsPostProcess; | |||
| utsTextSuite, utsUtils, utsConstants; | |||
| type | |||
| TMainForm = class(TForm) | |||
| @@ -18,8 +18,9 @@ type | |||
| fContext: TglcContext; | |||
| ftsContext: TtsContext; | |||
| ftsRenderer: TtsRendererOpenGL; | |||
| ftsCreator1: TtsFontGeneratorGDI; | |||
| ftsCreator2: TtsFontGeneratorGDI; | |||
| ftsCreator: TtsFontCreatorGDI; | |||
| ftsPostProcessor1: TtsPostProcessorList; | |||
| ftsPostProcessor2: TtsPostProcessorList; | |||
| ftsFont1: TtsFont; | |||
| ftsFont2: TtsFont; | |||
| procedure Render; | |||
| @@ -43,7 +44,7 @@ const | |||
| procedure TMainForm.FormCreate(Sender: TObject); | |||
| var | |||
| pf: TglcContextPixelFormatSettings; | |||
| pp: TtsPostProcessStep; | |||
| pp: TtsPostProcessor; | |||
| img: TtsImage; | |||
| const | |||
| @@ -59,44 +60,52 @@ begin | |||
| fContext.BuildContext; | |||
| ftsContext := TtsContext.Create; | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatRGBA8); | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, TtsFormat.tsFormatRGBA8); | |||
| ftsCreator1 := TtsFontGeneratorGDI.Create(ftsContext); | |||
| ftsFont1 := ftsCreator1.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', ftsRenderer, 40, [], tsAANormal); | |||
| // post processors | |||
| ftsPostProcessor1 := TtsPostProcessorList.Create(ftsContext, true); | |||
| ftsPostProcessor2 := TtsPostProcessorList.Create(ftsContext, true); | |||
| pp := TtsPostProcessFillColor.Create(tsColor4f(0, 0, 0, 1), TS_MODES_REPLACE_ALL, TS_CHANNELS_RGB); | |||
| pp.AddUsageChars(tsUsageExclude, 'Lorem'); | |||
| ftsCreator1.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorFillColor.Create(ftsContext, tsColor4f(0, 0, 0, 1), TS_IMAGE_MODES_REPLACE_ALL, TS_COLOR_CHANNELS_RGB); | |||
| pp.AddChars(TtsCharRangeUsage.tsUsageExclude, 'Lorem'); | |||
| ftsPostProcessor1.Add(pp); | |||
| pp := TtsPostProcessFillColor.Create(tsColor4f(1.0, 0.0, 0.0, 1.0), TS_MODES_MODULATE_ALL, TS_CHANNELS_RGB); | |||
| pp.AddUsageChars(tsUsageInclude, 'Lorem'); | |||
| ftsCreator1.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorFillColor.Create(ftsContext, tsColor4f(1.0, 0.0, 0.0, 1.0), TS_IMAGE_MODES_MODULATE_ALL, TS_COLOR_CHANNELS_RGB); | |||
| pp.AddChars(TtsCharRangeUsage.tsUsageInclude, 'Lorem'); | |||
| ftsPostProcessor1.Add(pp); | |||
| img := TtsImage.Create; | |||
| img.CreateEmpty(tsFormatAlpha8, 4, 4); | |||
| img.CreateEmpty(TtsFormat.tsFormatAlpha8, 4, 4); | |||
| Move(PATTER_DATA[0], img.Data^, 16); | |||
| pp := TtsPostProcessFillPattern.Create(img, true, 0, 0, TS_MODES_MODULATE_ALL, TS_CHANNELS_RGBA); | |||
| pp.AddUsageChars(tsUsageInclude, 'Lorem'); | |||
| ftsCreator1.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorFillPattern.Create(ftsContext, img, true, tsPosition(0, 0), TS_IMAGE_MODES_MODULATE_ALL, TS_COLOR_CHANNELS_RGBA); | |||
| pp.AddChars(TtsCharRangeUsage.tsUsageInclude, 'Lorem'); | |||
| ftsPostProcessor2.Add(pp); | |||
| ftsCreator2 := TtsFontGeneratorGDI.Create(ftsContext); | |||
| ftsFont2 := ftsCreator2.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', ftsRenderer, 40, [tsStyleStrikeout], tsAANormal); | |||
| pp := TtsPostProcessorFillColor.Create(ftsContext, tsColor4f(0, 0, 0.5, 1), TS_IMAGE_MODES_REPLACE_ALL, TS_COLOR_CHANNELS_RGB); | |||
| pp.AddChars(TtsCharRangeUsage.tsUsageExclude, 'e'); | |||
| ftsPostProcessor2.Add(pp); | |||
| pp := TtsPostProcessFillColor.Create(tsColor4f(0, 0, 0.5, 1), TS_MODES_REPLACE_ALL, TS_CHANNELS_RGB); | |||
| pp.AddUsageChars(tsUsageExclude, 'e'); | |||
| ftsCreator2.AddPostProcessStep(pp); | |||
| pp := TtsPostProcessorBorder.Create(ftsContext, 3.0, 0.5, tsColor4f(0.0, 0.5, 0.0, 1.0), true); | |||
| pp.AddChars(TtsCharRangeUsage.tsUsageInclude, 'e'); | |||
| ftsPostProcessor2.Add(pp); | |||
| pp := TtsPostProcessBorder.Create(3.0, 0.5, tsColor4f(0.0, 0.5, 0.0, 1.0), true); | |||
| pp.AddUsageChars(tsUsageInclude, 'e'); | |||
| ftsCreator2.AddPostProcessStep(pp); | |||
| // font creator and fonts | |||
| ftsCreator := TtsFontCreatorGDI.Create(ftsContext); | |||
| ftsFont1 := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', 40, [], TtsAntiAliasing.tsAANormal); | |||
| ftsFont1.PostProcessor := ftsPostProcessor1; | |||
| ftsFont2 := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', 40, [TtsFontStyle.tsStyleStrikeout], TtsAntiAliasing.tsAANormal); | |||
| ftsFont2.PostProcessor := ftsPostProcessor2; | |||
| end; | |||
| procedure TMainForm.FormDestroy(Sender: TObject); | |||
| begin | |||
| FreeAndNil(ftsFont2); | |||
| FreeAndNil(ftsCreator2); | |||
| FreeAndNil(ftsFont1); | |||
| FreeAndNil(ftsCreator1); | |||
| FreeAndNil(ftsCreator); | |||
| FreeAndNil(ftsPostProcessor2); | |||
| FreeAndNil(ftsPostProcessor1); | |||
| FreeAndNil(ftsRenderer); | |||
| FreeAndNil(ftsContext); | |||
| FreeAndNil(fContext); | |||
| @@ -127,9 +136,9 @@ begin | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| block := ftsRenderer.BeginBlock(10, 10, ClientWidth-20, ClientHeight-20, [tsBlockFlagWordWrap]); | |||
| block := ftsRenderer.BeginBlock(10, 10, ClientWidth-20, ClientHeight-20, [TtsBlockFlag.tsBlockFlagWordWrap]); | |||
| try | |||
| block.HorzAlign := tsHorzAlignJustify; | |||
| block.HorzAlign := TtsHorzAlignment.tsHorzAlignJustify; | |||
| block.ChangeFont(ftsFont1); | |||
| block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | |||
| block.TextOutW(TEST_TEXT + sLineBreak); | |||
| @@ -43,6 +43,7 @@ | |||
| <ComponentName Value="MainForm"/> | |||
| <HasResources Value="True"/> | |||
| <ResourceBaseClass Value="Form"/> | |||
| <UnitName Value="uMainForm"/> | |||
| </Unit1> | |||
| </Units> | |||
| </ProjectOptions> | |||
| @@ -53,7 +54,7 @@ | |||
| <Filename Value="SimpleFreeType"/> | |||
| </Target> | |||
| <SearchPaths> | |||
| <IncludeFiles Value="$(ProjOutDir)"/> | |||
| <IncludeFiles Value="$(ProjOutDir);..\..\inc"/> | |||
| <OtherUnitFiles Value="..\utils;..\.."/> | |||
| <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | |||
| </SearchPaths> | |||
| @@ -7,7 +7,7 @@ interface | |||
| uses | |||
| Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | |||
| uglcContext, | |||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorFreeType, utsTypes; | |||
| utsTextSuite, utsUtils; | |||
| type | |||
| TMainForm = class(TForm) | |||
| @@ -18,7 +18,7 @@ type | |||
| fContext: TglcContext; | |||
| ftsContext: TtsContext; | |||
| ftsRenderer: TtsRendererOpenGL; | |||
| ftsCreator: TtsFontGeneratorFreeType; | |||
| ftsCreator: TtsFontCreatorFreeType; | |||
| ftsFont: TtsFont; | |||
| procedure Render; | |||
| public | |||
| @@ -47,9 +47,9 @@ begin | |||
| fContext.BuildContext; | |||
| ftsContext := TtsContext.Create; | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatAlpha8); | |||
| ftsCreator := TtsFontGeneratorFreeType.Create(ftsContext); | |||
| ftsFont := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', ftsRenderer, 20, [], tsAANormal); | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, TtsFormat.tsFormatAlpha8); | |||
| ftsCreator := TtsFontCreatorFreeType.Create(ftsContext); | |||
| ftsFont := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', 20, [], TtsAntiAliasing.tsAANormal); | |||
| end; | |||
| procedure TMainForm.FormDestroy(Sender: TObject); | |||
| @@ -86,9 +86,9 @@ begin | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| block := ftsRenderer.BeginBlock(10, 10, ClientWidth-20, ClientHeight-20, [tsBlockFlagWordWrap]); | |||
| block := ftsRenderer.BeginBlock(10, 10, ClientWidth-20, ClientHeight-20, [TtsBlockFlag.tsBlockFlagWordWrap]); | |||
| try | |||
| block.HorzAlign := tsHorzAlignJustify; | |||
| block.HorzAlign := TtsHorzAlignment.tsHorzAlignJustify; | |||
| block.ChangeFont(ftsFont); | |||
| block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | |||
| block.TextOutW(TEST_TEXT); | |||
| @@ -32,7 +32,7 @@ | |||
| <PackageName Value="LCL"/> | |||
| </Item1> | |||
| </RequiredPackages> | |||
| <Units Count="2"> | |||
| <Units Count="21"> | |||
| <Unit0> | |||
| <Filename Value="SimpleGDI.lpr"/> | |||
| <IsPartOfProject Value="True"/> | |||
| @@ -43,7 +43,103 @@ | |||
| <ComponentName Value="MainForm"/> | |||
| <HasResources Value="True"/> | |||
| <ResourceBaseClass Value="Form"/> | |||
| <UnitName Value="uMainForm"/> | |||
| </Unit1> | |||
| <Unit2> | |||
| <Filename Value="..\..\utsContext.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsContext"/> | |||
| </Unit2> | |||
| <Unit3> | |||
| <Filename Value="..\..\utsTextSuite.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsTextSuite"/> | |||
| </Unit3> | |||
| <Unit4> | |||
| <Filename Value="..\..\utsUtils.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsUtils"/> | |||
| </Unit4> | |||
| <Unit5> | |||
| <Filename Value="..\..\utsTypes.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsTypes"/> | |||
| </Unit5> | |||
| <Unit6> | |||
| <Filename Value="..\..\utsRenderer.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsRenderer"/> | |||
| </Unit6> | |||
| <Unit7> | |||
| <Filename Value="..\..\utsTextBlock.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsTextBlock"/> | |||
| </Unit7> | |||
| <Unit8> | |||
| <Filename Value="..\..\utsFont.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsFont"/> | |||
| </Unit8> | |||
| <Unit9> | |||
| <Filename Value="..\..\utsFontCreator.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsFontCreator"/> | |||
| </Unit9> | |||
| <Unit10> | |||
| <Filename Value="..\..\utsChar.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsChar"/> | |||
| </Unit10> | |||
| <Unit11> | |||
| <Filename Value="..\..\utsImage.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsImage"/> | |||
| </Unit11> | |||
| <Unit12> | |||
| <Filename Value="..\..\utsConstants.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsConstants"/> | |||
| </Unit12> | |||
| <Unit13> | |||
| <Filename Value="..\..\utsPostProcessor.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsPostProcessor"/> | |||
| </Unit13> | |||
| <Unit14> | |||
| <Filename Value="..\..\utsCodePages.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsCodePages"/> | |||
| </Unit14> | |||
| <Unit15> | |||
| <Filename Value="..\..\utsCharCache.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsCharCache"/> | |||
| </Unit15> | |||
| <Unit16> | |||
| <Filename Value="..\..\utsOpenGLUtils.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsOpenGLUtils"/> | |||
| </Unit16> | |||
| <Unit17> | |||
| <Filename Value="..\..\utsRendererOpenGL.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsRendererOpenGL"/> | |||
| </Unit17> | |||
| <Unit18> | |||
| <Filename Value="..\..\utsRendererOpenGLES.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsRendererOpenGLES"/> | |||
| </Unit18> | |||
| <Unit19> | |||
| <Filename Value="..\..\utsFontCreatorFreeType.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsFontCreatorFreeType"/> | |||
| </Unit19> | |||
| <Unit20> | |||
| <Filename Value="..\..\utsFontCreatorGDI.pas"/> | |||
| <IsPartOfProject Value="True"/> | |||
| <UnitName Value="utsFontCreatorGDI"/> | |||
| </Unit20> | |||
| </Units> | |||
| </ProjectOptions> | |||
| <CompilerOptions> | |||
| @@ -53,7 +149,7 @@ | |||
| <Filename Value="SimpleGDI"/> | |||
| </Target> | |||
| <SearchPaths> | |||
| <IncludeFiles Value="$(ProjOutDir)"/> | |||
| <IncludeFiles Value="$(ProjOutDir);..\..\inc"/> | |||
| <OtherUnitFiles Value="..\utils;..\.."/> | |||
| <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | |||
| </SearchPaths> | |||
| @@ -64,6 +160,11 @@ | |||
| </Win32> | |||
| </Options> | |||
| </Linking> | |||
| <Other> | |||
| <CompilerMessages> | |||
| <IgnoredMessages idx5024="True"/> | |||
| </CompilerMessages> | |||
| </Other> | |||
| </CompilerOptions> | |||
| <Debugging> | |||
| <Exceptions Count="3"> | |||
| @@ -7,8 +7,7 @@ uses | |||
| cthreads, | |||
| {$ENDIF}{$ENDIF} | |||
| Interfaces, // this includes the LCL widgetset | |||
| Forms, uMainForm | |||
| { you can add units after this }; | |||
| Forms, uMainForm; | |||
| {$R *.res} | |||
| @@ -6,8 +6,8 @@ interface | |||
| uses | |||
| Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | |||
| uglcContext, | |||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorGDI, utsTypes; | |||
| uglcContext, utsUtils, | |||
| utsTextSuite; | |||
| type | |||
| TMainForm = class(TForm) | |||
| @@ -18,7 +18,7 @@ type | |||
| fContext: TglcContext; | |||
| ftsContext: TtsContext; | |||
| ftsRenderer: TtsRendererOpenGL; | |||
| ftsCreator: TtsFontGeneratorGDI; | |||
| ftsCreator: TtsFontCreatorGDI; | |||
| ftsFont: TtsFont; | |||
| procedure Render; | |||
| public | |||
| @@ -47,9 +47,9 @@ begin | |||
| fContext.BuildContext; | |||
| ftsContext := TtsContext.Create; | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatAlpha8); | |||
| ftsCreator := TtsFontGeneratorGDI.Create(ftsContext); | |||
| ftsFont := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', ftsRenderer, 20, [], tsAANormal); | |||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, TtsFormat.tsFormatAlpha8); | |||
| ftsCreator := TtsFontCreatorGDI.Create(ftsContext); | |||
| ftsFont := ftsCreator.GetFontByFile(ExtractFilePath(Application.ExeName) + '../Prototype.ttf', 20, [], TtsAntiAliasing.tsAANormal); | |||
| end; | |||
| procedure TMainForm.FormDestroy(Sender: TObject); | |||
| @@ -86,9 +86,9 @@ begin | |||
| glEnable(GL_BLEND); | |||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |||
| block := ftsRenderer.BeginBlock(10, 10, ClientWidth-20, ClientHeight-20, [tsBlockFlagWordWrap]); | |||
| block := ftsRenderer.BeginBlock(10, 10, ClientWidth-20, ClientHeight-20, [TtsBlockFlag.tsBlockFlagWordWrap]); | |||
| try | |||
| block.HorzAlign := tsHorzAlignJustify; | |||
| block.HorzAlign := TtsHorzAlignment.tsHorzAlignJustify; | |||
| block.ChangeFont(ftsFont); | |||
| block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | |||
| block.TextOutW(TEST_TEXT); | |||
| @@ -0,0 +1,4 @@ | |||
| {$DEFINE TS_ENABLE_OPENGL_SUPPORT} | |||
| {$DEFINE TS_ENABLE_OPENGLES_SUPPORT} | |||
| {$DEFINE TS_ENABLE_GDI_SUPPORT} | |||
| {$DEFINE TS_ENABLE_FREETYPE_SUPPORT} | |||
| @@ -0,0 +1,37 @@ | |||
| unit utsChar; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTypes; | |||
| type | |||
| TtsChar = class(TObject) | |||
| private | |||
| fCharCode: WideChar; | |||
| fGlyphMetric: TtsGlyphMetric; | |||
| protected | |||
| fRenderRef: TtsRenderRef; | |||
| public | |||
| property CharCode: WideChar read fCharCode; | |||
| property RenderRef: TtsRenderRef read fRenderRef; | |||
| property GlyphMetric: TtsGlyphMetric read fGlyphMetric write fGlyphMetric; | |||
| constructor Create(const aCharCode: WideChar); | |||
| end; | |||
| implementation | |||
| constructor TtsChar.Create(const aCharCode: WideChar); | |||
| begin | |||
| inherited Create; | |||
| fCharCode := aCharCode; | |||
| end; | |||
| end. | |||
| @@ -0,0 +1,508 @@ | |||
| unit utsCharCache; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsChar, utsFont, utsUtils, utsContext, utsTypes, utsImage; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsCharArray = packed record | |||
| Chars: array [Byte] of TtsChar; | |||
| Count: Byte; | |||
| end; | |||
| PtsCharArray = ^TtsCharArray; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsRenderRefGenerator = class(TtsRefManager) | |||
| private | |||
| fContext: TtsContext; | |||
| fFormat: TtsFormat; | |||
| public | |||
| property Context: TtsContext read fContext; | |||
| property Format: TtsFormat read fFormat; | |||
| function CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef; virtual; abstract; | |||
| procedure FreeRenderRef(const aRenderRef: TtsRenderRef); virtual; abstract; | |||
| constructor Create(const aContext: TtsContext; const aFormat: TtsFormat); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsChars = class(TObject) | |||
| private | |||
| fRenderRefGenerator: TtsRenderRefGenerator; | |||
| fFont: TtsFont; | |||
| fCanCreate: Boolean; | |||
| fChars: array[Byte] of PtsCharArray; | |||
| function GenerateChar(const aCharCode: WideChar): TtsChar; | |||
| public | |||
| function GetChar(const aCharCode: WideChar): TtsChar; | |||
| function AddChar(const aCharCode: WideChar): TtsChar; | |||
| procedure DelChar(const aCharCode: WideChar); | |||
| procedure AddCharRange(const aStart, aStop: WideChar); | |||
| procedure DelCharRange(const aStart, aStop: WideChar); | |||
| procedure Clear; | |||
| public | |||
| property CanCreate: Boolean read fCanCreate write fCanCreate; | |||
| property Char[const aCharCode: WideChar]: TtsChar read GetChar; | |||
| function GetTextWidthW(aText: PWideChar): Integer; | |||
| function GetTextWidthA(aText: PAnsiChar): Integer; | |||
| constructor Create(const aRenderRefGen: TtsRenderRefGenerator; const aFont: TtsFont); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| PtsCharCacheItem = ^TtsCharCacheItem; | |||
| TtsCharCacheItem = packed record | |||
| key: TtsFont; | |||
| val: TtsChars; | |||
| end; | |||
| TtsCharCache = class(TtsRefManager) | |||
| private | |||
| fRenderRefGenerator: TtsRenderRefGenerator; | |||
| fItems: TList; | |||
| function GetChars(const aKey: TtsFont): TtsChars; | |||
| function Find(const aMin, aMax: Integer; const aKey: TtsFont; out aIndex: Integer): Integer; | |||
| protected | |||
| procedure DelSlave(const aSlave: TtsRefManager); override; | |||
| public | |||
| property Chars[const aKey: TtsFont]: TtsChars read GetChars; | |||
| procedure Clear; | |||
| constructor Create(const aRenderRefGen: TtsRenderRefGenerator); | |||
| destructor Destroy; override; | |||
| end; | |||
| implementation | |||
| uses | |||
| Math, | |||
| utsConstants; | |||
| type | |||
| TtsWritableChar = class(TtsChar) | |||
| public | |||
| property RenderRef: TtsRenderRef read fRenderRef write fRenderRef; | |||
| end; | |||
| {$IFNDEF fpc} | |||
| {$IFDEF WIN64} | |||
| PtrUInt = System.UInt64; | |||
| {$ELSE} | |||
| PtrUInt = Cardinal; | |||
| {$ENDIF} | |||
| {$ENDIF} | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsChars////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsChars.GenerateChar(const aCharCode: WideChar): TtsChar; | |||
| var | |||
| GlyphSize: TtsPosition; | |||
| CharImage: TtsImage; | |||
| m: TtsGlyphMetric; | |||
| c: TtsWritableChar; | |||
| procedure FillLine(aData: PByte); | |||
| var | |||
| w, i: Integer; | |||
| c: TtsColor4f; | |||
| tmp: PByte; | |||
| begin | |||
| w := CharImage.Width; | |||
| while (w > 0) do begin | |||
| tmp := aData; | |||
| tsFormatUnmap(CharImage.Format, tmp, c); | |||
| for i := 0 to 3 do | |||
| c.arr[i] := 1.0; | |||
| tsFormatMap(CharImage.Format, aData, c); | |||
| dec(w); | |||
| end; | |||
| end; | |||
| procedure DrawLine(aLinePosition, aLineSize: Integer); | |||
| var | |||
| ImgSize, ImgPos, Origin: TtsPosition; | |||
| Rect: TtsRect; | |||
| YOffset, y: Integer; | |||
| begin | |||
| if aLineSize <= 0 then | |||
| exit; | |||
| aLinePosition := aLinePosition - aLineSize; | |||
| // calculate width and height | |||
| ImgPos := tsPosition(0, 0); | |||
| ImgSize := tsPosition(CharImage.Width, CharImage.Height); | |||
| Origin := m.GlyphOrigin; | |||
| Rect := m.GlyphRect; | |||
| // expand left rect border to origin | |||
| if (Origin.x > 0) then begin | |||
| dec(Rect.Left, Origin.x); | |||
| Origin.x := 0; | |||
| end; | |||
| // expand right rect border to advanced | |||
| if (Rect.Right - Rect.Left < m.Advance) then begin | |||
| Rect.Right := Rect.Left + m.Advance; | |||
| end; | |||
| // expand bottom rect border | |||
| if (Origin.y - aLinePosition > Rect.Bottom) then begin | |||
| Rect.Bottom := Origin.y - aLinePosition; | |||
| end; | |||
| // expand top rect border | |||
| if (Origin.y - aLinePosition - aLineSize < Rect.Top) then begin | |||
| Rect.Top := Origin.y - aLinePosition - aLineSize; | |||
| Origin.y := aLinePosition + aLineSize; | |||
| end; | |||
| // update image size | |||
| if (Rect.Right - Rect.Left > ImgSize.x) then begin | |||
| ImgSize.x := Rect.Right - Rect.Left; | |||
| ImgPos.x := Max(-Rect.Left, 0); | |||
| inc(Rect.Left, ImgPos.x); | |||
| inc(Rect.Right, ImgPos.x); | |||
| end; | |||
| if (Rect.Bottom - Rect.Top > ImgSize.y) then begin | |||
| ImgSize.y := Rect.Bottom - Rect.Top; | |||
| ImgPos.y := Max(-Rect.Top, 0); | |||
| inc(Rect.Top, ImgPos.y); | |||
| inc(Rect.Bottom, ImgPos.y); | |||
| end; | |||
| CharImage.Resize(ImgSize.x, ImgSize.y, ImgPos.x, ImgPos.y); | |||
| // draw lines | |||
| YOffset := Rect.Top + Origin.y - aLinePosition; | |||
| for y := 1 to aLineSize do | |||
| FillLine(CharImage.ScanLine[YOffset - y]); | |||
| // move glyph rect | |||
| m.GlyphOrigin := Origin; | |||
| m.GlyphRect := Rect; | |||
| end; | |||
| begin | |||
| result := nil; | |||
| if (aCharCode <> #0) and | |||
| (not fFont.GetGlyphMetrics(aCharCode, m.GlyphOrigin, GlyphSize, m.Advance) or | |||
| not ((m.GlyphOrigin.x <> 0) or | |||
| (m.GlyphOrigin.y <> 0) or | |||
| (GlyphSize.x <> 0) or | |||
| (GlyphSize.y <> 0) or | |||
| (m.Advance <> 0))) then | |||
| exit; | |||
| CharImage := TtsImage.Create; | |||
| try | |||
| if (aCharCode = #0) then begin | |||
| CharImage.CreateEmpty(fRenderRefGenerator.Format, 3, 1); | |||
| m.GlyphOrigin := tsPosition(0, 1); | |||
| m.Advance := 1; | |||
| end else if (GlyphSize.x > 0) and (GlyphSize.y > 0) then | |||
| fFont.GetCharImage(aCharCode, CharImage, fRenderRefGenerator.Format); | |||
| if CharImage.IsEmpty and ([tsStyleUnderline, tsStyleStrikeout] * fFont.Metric.Style <> []) then begin | |||
| CharImage.CreateEmpty(fRenderRefGenerator.Format, max(m.Advance, 1), 1); | |||
| m.GlyphOrigin.y := 1; | |||
| end; | |||
| c := TtsWritableChar.Create(aCharCode); | |||
| try | |||
| if (aCharCode = #0) | |||
| then m.GlyphRect := tsRect(1, 0, 2, 1) | |||
| else m.GlyphRect := tsRect(0, 0, CharImage.Width, CharImage.Height); | |||
| try | |||
| if (tsStyleUnderline in fFont.Metric.Style) then | |||
| DrawLine(fFont.Metric.UnderlinePos, fFont.Metric.UnderlineSize); | |||
| if (tsStyleStrikeout in fFont.Metric.Style) then | |||
| DrawLine(fFont.Metric.StrikeoutPos, fFont.Metric.StrikeoutSize); | |||
| except | |||
| CharImage.FillColor(tsColor4f(1, 0, 0, 0), TS_COLOR_CHANNELS_RGB, TS_IMAGE_MODES_MODULATE_ALPHA); | |||
| end; | |||
| c.GlyphMetric := m; | |||
| if Assigned(fFont.PostProcessor) then | |||
| fFont.PostProcessor.Execute(c, CharImage); | |||
| c.RenderRef := fRenderRefGenerator.CreateRenderRef(c, CharImage); | |||
| result := c; | |||
| except | |||
| FreeAndNil(c); | |||
| end; | |||
| finally | |||
| FreeAndNil(CharImage); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsChars.GetChar(const aCharCode: WideChar): TtsChar; | |||
| var | |||
| arr: PtsCharArray; | |||
| begin | |||
| arr := fChars[(Ord(aCharCode) shr 8) and $FF]; | |||
| if Assigned(arr) then | |||
| result := arr^.Chars[Ord(aCharCode) and $FF] | |||
| else | |||
| result := nil; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsChars.AddChar(const aCharCode: WideChar): TtsChar; | |||
| var | |||
| h, l: Integer; | |||
| arr: PtsCharArray; | |||
| begin | |||
| result := GetChar(aCharCode); | |||
| if not Assigned(result) and fCanCreate then begin | |||
| result := GenerateChar(aCharCode); | |||
| if Assigned(result) then begin | |||
| h := (Ord(aCharCode) shr 8) and $FF; | |||
| arr := fChars[h]; | |||
| if not Assigned(arr) then begin | |||
| New(arr); | |||
| FillChar(arr^, SizeOf(arr^), 0); | |||
| fChars[h] := arr; | |||
| end; | |||
| l := Ord(aCharCode) and $FF; | |||
| arr^.Chars[l] := result; | |||
| inc(arr^.Count); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsChars.DelChar(const aCharCode: WideChar); | |||
| var | |||
| h, l: Integer; | |||
| c: TtsChar; | |||
| arr: PtsCharArray; | |||
| begin | |||
| // find char array | |||
| h := (Ord(aCharCode) shr 8) and $FF; | |||
| arr := fChars[h]; | |||
| if not Assigned(arr) then | |||
| exit; | |||
| // find char | |||
| l := Ord(aCharCode) and $FF; | |||
| c := arr^.Chars[l]; | |||
| if not Assigned(c) then | |||
| exit; | |||
| // remove char | |||
| arr^.Chars[l] := nil; | |||
| dec(arr^.Count); | |||
| if (arr^.Count <= 0) then begin | |||
| fChars[h] := nil; | |||
| Dispose(arr); | |||
| end; | |||
| if Assigned(c.RenderRef) then | |||
| fRenderRefGenerator.FreeRenderRef(c.RenderRef); | |||
| FreeAndNil(c); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsChars.AddCharRange(const aStart, aStop: WideChar); | |||
| var | |||
| c: WideChar; | |||
| begin | |||
| for c := aStart to aStop do | |||
| AddChar(c); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsChars.DelCharRange(const aStart, aStop: WideChar); | |||
| var | |||
| c: WideChar; | |||
| begin | |||
| for c := aStart to aStop do | |||
| DelChar(c); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsChars.Clear; | |||
| var | |||
| h, l: Integer; | |||
| c: TtsChar; | |||
| arr: PtsCharArray; | |||
| begin | |||
| for h := Low(fChars) to High(fChars) do begin | |||
| arr := fChars[h]; | |||
| if Assigned(arr) then begin | |||
| for l := Low(arr^.Chars) to High(arr^.Chars) do begin | |||
| c := arr^.Chars[l]; | |||
| if Assigned(c) then begin | |||
| if Assigned(c.RenderRef) then | |||
| fRenderRefGenerator.FreeRenderRef(c.RenderRef); | |||
| FreeAndNil(c); | |||
| end; | |||
| end; | |||
| Dispose(arr); | |||
| fChars[h] := nil; | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsChars.GetTextWidthW(aText: PWideChar): Integer; | |||
| var | |||
| c: TtsChar; | |||
| begin | |||
| result := 0; | |||
| if not Assigned(aText) then | |||
| exit; | |||
| while (aText^ <> #0) do begin | |||
| c := AddChar(aText^); | |||
| if not Assigned(c) then | |||
| c := AddChar(fRenderRefGenerator.Context.DefaultChar); | |||
| if Assigned(c) then begin | |||
| if (result > 0) then | |||
| result := result + fFont.CharSpacing; | |||
| result := result + c.GlyphMetric.Advance; | |||
| end; | |||
| inc(aText); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsChars.GetTextWidthA(aText: PAnsiChar): Integer; | |||
| var | |||
| tmp: PWideChar; | |||
| begin | |||
| tmp := fRenderRefGenerator.Context.AnsiToWide(aText); | |||
| try | |||
| result := GetTextWidthW(tmp); | |||
| finally | |||
| tsStrDispose(tmp); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsChars.Create(const aRenderRefGen: TtsRenderRefGenerator; const aFont: TtsFont); | |||
| begin | |||
| inherited Create; | |||
| fRenderRefGenerator := aRenderRefGen; | |||
| fFont := aFont; | |||
| fCanCreate := true; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsChars.Destroy; | |||
| begin | |||
| Clear; | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsRenderRefGenerator///////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsRenderRefGenerator.Create(const aContext: TtsContext; const aFormat: TtsFormat); | |||
| begin | |||
| inherited Create(aContext); | |||
| fContext := aContext; | |||
| fFormat := aFormat; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsCharCache////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsCharCache.GetChars(const aKey: TtsFont): TtsChars; | |||
| var | |||
| pos, index: Integer; | |||
| p: PtsCharCacheItem; | |||
| begin | |||
| pos := Find(0, fItems.Count-1, aKey, index); | |||
| if (pos < 0) then begin | |||
| result := TtsChars.Create(fRenderRefGenerator, aKey); | |||
| aKey.AddMaster(self); | |||
| new(p); | |||
| p^.key := aKey; | |||
| p^.val := result; | |||
| fItems.Insert(index, p); | |||
| end else | |||
| result := PtsCharCacheItem(fItems[pos])^.val; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsCharCache.Find(const aMin, aMax: Integer; const aKey: TtsFont; out aIndex: Integer): Integer; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| if (aMin <= aMax) then begin | |||
| i := aMin + Trunc((aMax - aMin) / 2); | |||
| if (aKey = PtsCharCacheItem(fItems[i])^.key) then | |||
| result := i | |||
| else if (PtrUInt(aKey) < PtrUInt(PtsCharCacheItem(fItems[i])^.key)) then | |||
| result := Find(aMin, i-1, aKey, aIndex) | |||
| else | |||
| result := Find(i+1, aMax, aKey, aIndex); | |||
| end else begin | |||
| result := -1; | |||
| aIndex := aMin; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsCharCache.DelSlave(const aSlave: TtsRefManager); | |||
| var | |||
| pos, index: Integer; | |||
| p: PtsCharCacheItem; | |||
| begin | |||
| pos := Find(0, fItems.Count-1, aSlave as TtsFont, index); | |||
| if (pos >= 0) then begin | |||
| p := PtsCharCacheItem(fItems[pos]); | |||
| fItems.Delete(pos); | |||
| FreeAndNil(p^.val); | |||
| Dispose(p); | |||
| end; | |||
| inherited DelSlave(aSlave); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsCharCache.Clear; | |||
| var | |||
| p: PtsCharCacheItem; | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to fItems.Count-1 do begin | |||
| p := PtsCharCacheItem(fItems[i]); | |||
| FreeAndNil(p^.val); | |||
| Dispose(p); | |||
| end; | |||
| fItems.Clear; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsCharCache.Create(const aRenderRefGen: TtsRenderRefGenerator); | |||
| begin | |||
| inherited Create(aRenderRefGen); | |||
| fRenderRefGenerator := aRenderRefGen; | |||
| fItems := TList.Create; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsCharCache.Destroy; | |||
| begin | |||
| Clear; | |||
| FreeAndNil(fItems); | |||
| inherited Destroy; | |||
| end; | |||
| end. | |||
| @@ -1,17 +1,13 @@ | |||
| unit utsCodePages; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, utsTypes; | |||
| type | |||
| PtsCodePageValues = ^TtsCodePageValues; | |||
| TtsCodePageValues = array [AnsiChar] of word; | |||
| utsTypes; | |||
| const | |||
| CP_8859_2 : TtsCodePageValues = ( | |||
| @@ -893,55 +889,6 @@ const | |||
| $0111, $00F1, $0323, $00F3, $00F4, $01A1, $00F6, $00F7, $00F8, $00F9, $00FA, $00FB, $00FC, $01B0, $20AB, $00FF | |||
| ); | |||
| const | |||
| ANSI_TO_WIDE_CODE_PAGE_LUT: array[TtsCodePage] of PtsCodePageValues = ( | |||
| nil, //tsUTF8 | |||
| nil, //tsISO_8859_1 | |||
| @CP_8859_2, //tsISO_8859_2 | |||
| @CP_8859_3, //tsISO_8859_3 | |||
| @CP_8859_4, //tsISO_8859_4 | |||
| @CP_8859_5, //tsISO_8859_5 | |||
| @CP_8859_6, //tsISO_8859_6 | |||
| @CP_8859_7, //tsISO_8859_7 | |||
| @CP_8859_8, //tsISO_8859_8 | |||
| @CP_8859_9, //tsISO_8859_9 | |||
| @CP_8859_10, //tsISO_8859_10 | |||
| @CP_8859_11, //tsISO_8859_11 | |||
| @CP_8859_13, //tsISO_8859_13 | |||
| @CP_8859_14, //tsISO_8859_14 | |||
| @CP_8859_15, //tsISO_8859_15 | |||
| @CP_8859_16, //tsISO_8859_16 | |||
| @CP_037, //tsISO_037 | |||
| @CP_437, //tsISO_437 | |||
| @CP_500, //tsISO_500 | |||
| @CP_737, //tsISO_737 | |||
| @CP_775, //tsISO_775 | |||
| @CP_850, //tsISO_850 | |||
| @CP_852, //tsISO_852 | |||
| @CP_855, //tsISO_855 | |||
| @CP_857, //tsISO_857 | |||
| @CP_860, //tsISO_860 | |||
| @CP_861, //tsISO_861 | |||
| @CP_862, //tsISO_862 | |||
| @CP_863, //tsISO_863 | |||
| @CP_864, //tsISO_864 | |||
| @CP_865, //tsISO_865 | |||
| @CP_866, //tsISO_866 | |||
| @CP_869, //tsISO_869 | |||
| @CP_874, //tsISO_874 | |||
| @CP_875, //tsISO_875 | |||
| @CP_1026, //tsISO_1026 | |||
| @CP_1250, //tsISO_1250 | |||
| @CP_1251, //tsISO_1251 | |||
| @CP_1252, //tsISO_1252 | |||
| @CP_1253, //tsISO_1253 | |||
| @CP_1254, //tsISO_1254 | |||
| @CP_1255, //tsISO_1255 | |||
| @CP_1256, //tsISO_1256 | |||
| @CP_1257, //tsISO_1257 | |||
| @CP_1258 //tsISO_1258 | |||
| ); | |||
| implementation | |||
| end. | |||
| @@ -0,0 +1,98 @@ | |||
| unit utsConstants; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTypes, utsUtils, utsCodePages; | |||
| const | |||
| TS_IMAGE_MODE_FUNCTIONS: array[TtsImageMode] of TtsBlendValueFunc = ( | |||
| tsBlendValueIgnore, | |||
| tsBlendValueReplace, | |||
| tsBlendValueModulate); | |||
| TS_IMAGE_MODES_REPLACE_ALL: TtsImageModes = ( | |||
| tsModeReplace, | |||
| tsModeReplace, | |||
| tsModeReplace, | |||
| tsModeReplace); | |||
| TS_IMAGE_MODES_MODULATE_ALPHA: TtsImageModes = ( | |||
| tsModeReplace, | |||
| tsModeReplace, | |||
| tsModeReplace, | |||
| tsModeModulate); | |||
| TS_IMAGE_MODES_MODULATE_ALL: TtsImageModes = ( | |||
| tsModeModulate, | |||
| tsModeModulate, | |||
| tsModeModulate, | |||
| tsModeModulate); | |||
| TS_COLOR_CHANNELS_RGB: TtsColorChannels = [ | |||
| tsChannelRed, | |||
| tsChannelGreen, | |||
| tsChannelBlue]; | |||
| TS_COLOR_CHANNELS_RGBA: TtsColorChannels = [ | |||
| tsChannelRed, | |||
| tsChannelGreen, | |||
| tsChannelBlue, | |||
| tsChannelAlpha]; | |||
| TS_CODE_PAGE_LUT: array[TtsCodePage] of PtsCodePageValues = ( | |||
| nil, //tsUTF8 | |||
| nil, //tsISO_8859_1 | |||
| @CP_8859_2, //tsISO_8859_2 | |||
| @CP_8859_3, //tsISO_8859_3 | |||
| @CP_8859_4, //tsISO_8859_4 | |||
| @CP_8859_5, //tsISO_8859_5 | |||
| @CP_8859_6, //tsISO_8859_6 | |||
| @CP_8859_7, //tsISO_8859_7 | |||
| @CP_8859_8, //tsISO_8859_8 | |||
| @CP_8859_9, //tsISO_8859_9 | |||
| @CP_8859_10, //tsISO_8859_10 | |||
| @CP_8859_11, //tsISO_8859_11 | |||
| @CP_8859_13, //tsISO_8859_13 | |||
| @CP_8859_14, //tsISO_8859_14 | |||
| @CP_8859_15, //tsISO_8859_15 | |||
| @CP_8859_16, //tsISO_8859_16 | |||
| @CP_037, //tsISO_037 | |||
| @CP_437, //tsISO_437 | |||
| @CP_500, //tsISO_500 | |||
| @CP_737, //tsISO_737 | |||
| @CP_775, //tsISO_775 | |||
| @CP_850, //tsISO_850 | |||
| @CP_852, //tsISO_852 | |||
| @CP_855, //tsISO_855 | |||
| @CP_857, //tsISO_857 | |||
| @CP_860, //tsISO_860 | |||
| @CP_861, //tsISO_861 | |||
| @CP_862, //tsISO_862 | |||
| @CP_863, //tsISO_863 | |||
| @CP_864, //tsISO_864 | |||
| @CP_865, //tsISO_865 | |||
| @CP_866, //tsISO_866 | |||
| @CP_869, //tsISO_869 | |||
| @CP_874, //tsISO_874 | |||
| @CP_875, //tsISO_875 | |||
| @CP_1026, //tsISO_1026 | |||
| @CP_1250, //tsISO_1250 | |||
| @CP_1251, //tsISO_1251 | |||
| @CP_1252, //tsISO_1252 | |||
| @CP_1253, //tsISO_1253 | |||
| @CP_1254, //tsISO_1254 | |||
| @CP_1255, //tsISO_1255 | |||
| @CP_1256, //tsISO_1256 | |||
| @CP_1257, //tsISO_1257 | |||
| @CP_1258 //tsISO_1258 | |||
| ); | |||
| TS_MATRIX_IDENTITY: TtsMatrix4f = ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)); | |||
| implementation | |||
| end. | |||
| @@ -0,0 +1,58 @@ | |||
| unit utsContext; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsUtils, utsTypes; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsContext = class(TtsRefManager) | |||
| private | |||
| fCodePage: TtsCodePage; | |||
| fDefaultChar: WideChar; | |||
| public | |||
| property CodePage: TtsCodePage read fCodePage write fCodePage; | |||
| property DefaultChar: WideChar read fDefaultChar write fDefaultChar; | |||
| function AnsiToWide(const aText: PAnsiChar): PWideChar; overload; | |||
| function AnsiToWide(const aText: PAnsiChar; const aLength: Integer): PWideChar; overload; | |||
| constructor Create; | |||
| end; | |||
| implementation | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsContext//////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsContext.AnsiToWide(const aText: PAnsiChar): PWideChar; | |||
| begin | |||
| result := AnsiToWide(aText, Length(aText)); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsContext.AnsiToWide(const aText: PAnsiChar; const aLength: Integer): PWideChar; | |||
| begin | |||
| result := nil; | |||
| if not Assigned(aText) then | |||
| exit; | |||
| result := tsStrAlloc(aLength); | |||
| tsAnsiToWide(result, aLength, aText, fCodePage, fDefaultChar); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsContext.Create; | |||
| begin | |||
| inherited Create(nil); | |||
| fCodePage := tsUTF8; | |||
| fDefaultChar := '?'; | |||
| end; | |||
| end. | |||
| @@ -0,0 +1,65 @@ | |||
| unit utsFont; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsUtils, utsFontCreator, utsTypes, utsPostProcessor, utsImage; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsFont = class(TtsMultiMasterRefManager) | |||
| private | |||
| fCreator: TtsFontCreator; | |||
| fMetric: TtsFontMetric; | |||
| fPostProcessor: TtsPostProcessor; | |||
| fTabWidth: Integer; | |||
| fCharSpacing: Integer; | |||
| fLineSpacing: Single; | |||
| protected | |||
| {%H-}constructor Create(const aCreator: TtsFontCreator; const aMetric: TtsFontMetric); | |||
| public | |||
| property Creator: TtsFontCreator read fCreator; | |||
| property Metric: TtsFontMetric read fMetric; | |||
| property PostProcessor: TtsPostProcessor read fPostProcessor write fPostProcessor; | |||
| property TabWidth: Integer read fTabWidth write fTabWidth; | |||
| property CharSpacing: Integer read fCharSpacing write fCharSpacing; | |||
| property LineSpacing: Single read fLineSpacing write fLineSpacing; | |||
| procedure GetTextMetric(out aMetric: TtsTextMetric); | |||
| procedure GetCharImage(const aCharCode: WideChar; const aCharImage: TtsImage; const aFormat: TtsFormat); virtual; abstract; | |||
| function GetGlyphMetrics(const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; virtual; abstract; | |||
| end; | |||
| implementation | |||
| /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsFont////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsFont.Create(const aCreator: TtsFontCreator; const aMetric: TtsFontMetric); | |||
| begin | |||
| inherited Create(aCreator); | |||
| fCreator := aCreator; | |||
| fMetric := aMetric; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsFont.GetTextMetric(out aMetric: TtsTextMetric); | |||
| begin | |||
| aMetric.Ascent := fMetric.Ascent; | |||
| aMetric.Descent := fMetric.Descent; | |||
| aMetric.ExternalLeading := fMetric.ExternalLeading; | |||
| aMetric.BaseLineOffset := fMetric.BaseLineOffset; | |||
| aMetric.CharSpacing := CharSpacing; | |||
| aMetric.LineHeight := fMetric.Ascent + fMetric.Descent + fMetric.ExternalLeading; | |||
| aMetric.LineSpacing := Trunc(fMetric.Size * fLineSpacing); | |||
| end; | |||
| end. | |||
| @@ -0,0 +1,36 @@ | |||
| unit utsFontCreator; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsUtils, utsContext; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsFontCreator = class(TtsRefManager) | |||
| private | |||
| fContext: TtsContext; | |||
| public | |||
| property Context: TtsContext read fContext; | |||
| constructor Create(const aContext: TtsContext); | |||
| end; | |||
| implementation | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsFontCreator//////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsFontCreator.Create(const aContext: TtsContext); | |||
| begin | |||
| inherited Create(aContext); | |||
| fContext := aContext; | |||
| end; | |||
| end. | |||
| @@ -1,14 +1,14 @@ | |||
| unit utsFontCreatorFreeType; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTextSuite, utsTypes, utsFreeType; | |||
| utsFreeType, utsFontCreator, utsFont, utsTypes, utsImage, utsContext; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| @@ -24,31 +24,25 @@ type | |||
| TtsFontFreeType = class(TtsFont) | |||
| private | |||
| fHandle: TtsFreeTypeFaceHandle; | |||
| protected | |||
| {%H-}constructor Create(const aHandle: TtsFreeTypeFaceHandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric); | |||
| public | |||
| constructor Create(const aHandle: TtsFreeTypeFaceHandle; const aRenderer: TtsRenderer; | |||
| const aGenerator: TtsFontGenerator; const aProperties: TtsFontProperties); | |||
| procedure GetCharImage(const aCharCode: WideChar; const aCharImage: TtsImage; const aFormat: TtsFormat); override; | |||
| function GetGlyphMetrics(const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; override; | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsFontGeneratorFreeType = class(TtsFontGenerator) | |||
| TtsFontCreatorFreeType = class(TtsFontCreator) | |||
| private | |||
| fHandle: FT_Library; | |||
| function ConvertFont(const aFont: TtsFont): TtsFontFreeType; | |||
| procedure LoadNames(const aFace: FT_Face; var aProperties: TtsFontProperties); | |||
| function CreateFont(const aFace: FT_Face; const aRenderer: TtsRenderer; const aSize: Integer; | |||
| const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| protected | |||
| function GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; | |||
| out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; override; | |||
| procedure GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; | |||
| const aCharImage: TtsImage); override; | |||
| procedure LoadNames(const aFace: FT_Face; var aMetric: TtsFontMetric); | |||
| function CreateFont(const aFace: FT_Face; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| public | |||
| function GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer; const aSize: Integer; | |||
| const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload; | |||
| function GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer; const aSize: Integer; | |||
| const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload; | |||
| function GetFontByFile(const aFilename: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload; | |||
| function GetFontByStream(const aStream: TStream; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; overload; | |||
| constructor Create(const aContext: TtsContext); | |||
| destructor Destroy; override; | |||
| @@ -57,7 +51,8 @@ type | |||
| implementation | |||
| uses | |||
| utsUtils, math; | |||
| Math, | |||
| utsUtils; | |||
| const | |||
| FT_SIZE_FACTOR = 64; | |||
| @@ -82,32 +77,150 @@ end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsFontFreeType/////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsFontFreeType.Create(const aHandle: TtsFreeTypeFaceHandle; const aRenderer: TtsRenderer; | |||
| const aGenerator: TtsFontGenerator; const aProperties: TtsFontProperties); | |||
| constructor TtsFontFreeType.Create(const aHandle: TtsFreeTypeFaceHandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric); | |||
| begin | |||
| inherited Create(aRenderer, aGenerator, aProperties); | |||
| inherited Create(aCreator, aMetric); | |||
| fHandle := aHandle; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsFontFreeType.Destroy; | |||
| procedure TtsFontFreeType.GetCharImage(const aCharCode: WideChar; const aCharImage: TtsImage; const aFormat: TtsFormat); | |||
| var | |||
| err: FT_Error; | |||
| g: FT_GlyphSlot; | |||
| b: PFT_Bitmap; | |||
| procedure CopyGray; | |||
| var | |||
| x, y: Integer; | |||
| src, dst: PByte; | |||
| c: TtsColor4f; | |||
| begin | |||
| aCharImage.CreateEmpty(aFormat, b^.width, b^.rows); | |||
| c := tsColor4f(1, 1, 1, 1); | |||
| for y := 0 to b^.rows-1 do begin | |||
| src := b^.buffer; | |||
| inc(src, y * b^.pitch); | |||
| dst := aCharImage.Scanline[y]; | |||
| for x := 0 to b^.width-1 do begin | |||
| c.a := src^ / $FF; | |||
| inc(src, 1); | |||
| tsFormatMap(aCharImage.Format, dst, c); | |||
| end; | |||
| end; | |||
| end; | |||
| procedure CopyMono; | |||
| var | |||
| x, y, i, cnt: Integer; | |||
| src, dst: PByte; | |||
| tmp: Byte; | |||
| c: TtsColor4f; | |||
| begin | |||
| aCharImage.CreateEmpty(aFormat, b^.width, b^.rows); | |||
| c := tsColor4f(1, 1, 1, 1); | |||
| for y := 0 to b^.rows-1 do begin | |||
| src := b^.buffer; | |||
| inc(src, y * b^.pitch); | |||
| dst := aCharImage.Scanline[y]; | |||
| x := b^.width; | |||
| while (x > 0) do begin | |||
| cnt := min(8, x); | |||
| tmp := src^; | |||
| inc(src, 1); | |||
| for i := 1 to cnt do begin | |||
| if ((tmp and $80) > 0) then | |||
| c.a := 1.0 | |||
| else | |||
| c.a := 0.0; | |||
| tmp := (tmp and not $80) shl 1; | |||
| tsFormatMap(aCharImage.Format, dst, c); | |||
| end; | |||
| dec(x, cnt); | |||
| end; | |||
| end; | |||
| end; | |||
| begin | |||
| FreeAndNil(fHandle); | |||
| inherited Destroy; | |||
| g := fHandle.fFace^.glyph; | |||
| if not (Metric.AntiAliasing in [tsAANormal, tsAANone]) then | |||
| raise Exception.Create('unknown anti aliasing'); | |||
| case Metric.AntiAliasing of | |||
| tsAANormal: | |||
| err := FT_Load_Char(fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT or FT_LOAD_RENDER); | |||
| tsAANone: | |||
| err := FT_Load_Char(fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME or FT_LOAD_TARGET_MONO or FT_LOAD_RENDER); | |||
| else | |||
| exit; | |||
| end; | |||
| if (err <> 0) then | |||
| raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err)); | |||
| if (g^.format <> FT_GLYPH_FORMAT_BITMAP) then | |||
| raise EtsException.Create('invalid glyph format'); | |||
| b := @g^.bitmap; | |||
| case b^.pixel_mode of | |||
| FT_PIXEL_MODE_MONO: | |||
| CopyMono; | |||
| FT_PIXEL_MODE_GRAY: | |||
| CopyGray; | |||
| else | |||
| raise EtsException.Create('unknown glyph bitmap format'); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsFontGeneratorFreeType////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsFontFreeType.GetGlyphMetrics(const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; | |||
| var | |||
| err: FT_Error; | |||
| begin | |||
| result := false; | |||
| aGlyphOrigin.x := 0; | |||
| aGlyphOrigin.x := 0; | |||
| aGlyphSize.x := 0; | |||
| aGlyphSize.y := 0; | |||
| aAdvance := 0; | |||
| case Metric.AntiAliasing of | |||
| tsAANormal: | |||
| err := FT_Load_Char(fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT); | |||
| tsAANone: | |||
| err := FT_Load_Char(fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME); | |||
| else | |||
| raise EtsException.Create('unknown anti aliasing'); | |||
| end; | |||
| case err of | |||
| FT_ERR_None: | |||
| { nop }; | |||
| FT_ERR_Invalid_Character_Code: | |||
| exit; | |||
| else | |||
| raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err)); | |||
| end; | |||
| result := true; | |||
| with fHandle.fFace^.glyph^.metrics do begin | |||
| aAdvance := horiAdvance div FT_SIZE_FACTOR; | |||
| aGlyphOrigin.x := horiBearingX div FT_SIZE_FACTOR; | |||
| aGlyphOrigin.y := horiBearingY div FT_SIZE_FACTOR; | |||
| aGlyphSize.x := width div FT_SIZE_FACTOR; | |||
| aGlyphSize.y := height div FT_SIZE_FACTOR; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsFontGeneratorFreeType.ConvertFont(const aFont: TtsFont): TtsFontFreeType; | |||
| destructor TtsFontFreeType.Destroy; | |||
| begin | |||
| if not (aFont is TtsFontFreeType) then | |||
| raise EtsException.Create('aFont need to be a TtsFontGDI object'); | |||
| result := (aFont as TtsFontFreeType); | |||
| FreeAndNil(fHandle); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsFontGeneratorFreeType.LoadNames(const aFace: FT_Face; var aProperties: TtsFontProperties); | |||
| //TtsFontCreatorFreeType////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsFontCreatorFreeType.LoadNames(const aFace: FT_Face; var aMetric: TtsFontMetric); | |||
| var | |||
| i, cnt: FT_Int; | |||
| err: FT_Error; | |||
| @@ -162,26 +275,26 @@ begin | |||
| case name.name_id of | |||
| TT_NAME_ID_COPYRIGHT: | |||
| if (aProperties.Copyright = '') then | |||
| aProperties.Copyright := Decode; | |||
| if (aMetric.Copyright = '') then | |||
| aMetric.Copyright := Decode; | |||
| TT_NAME_ID_FONT_FAMILY: | |||
| if (aProperties.Fontname = '') then | |||
| aProperties.Fontname := Decode; | |||
| if (aMetric.Fontname = '') then | |||
| aMetric.Fontname := Decode; | |||
| TT_NAME_ID_FULL_NAME: | |||
| if (aProperties.FullName = '') then | |||
| aProperties.FullName := Decode; | |||
| if (aMetric.FullName = '') then | |||
| aMetric.FullName := Decode; | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsFontGeneratorFreeType.CreateFont(const aFace: FT_Face; const aRenderer: TtsRenderer; const aSize: Integer; | |||
| const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| function TtsFontCreatorFreeType.CreateFont(const aFace: FT_Face; const aSize: Integer; const aStyle: TtsFontStyles; | |||
| const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| var | |||
| err: FT_Error; | |||
| prop: TtsFontProperties; | |||
| metric: TtsFontMetric; | |||
| os2: PTT_OS2; | |||
| hz: PTT_HoriHeader; | |||
| begin | |||
| @@ -189,173 +302,45 @@ begin | |||
| if (err <> 0) then | |||
| raise EtsException.Create('unable to set char size: error=' + IntToStr(err)); | |||
| FillByte(prop{%H-}, SizeOf(prop), 0); | |||
| prop.AntiAliasing := tsAANormal; | |||
| prop.FaceName := aFace^.family_name; | |||
| prop.StyleName := aFace^.style_name; | |||
| LoadNames(aFace, prop); | |||
| FillChar(metric{%H-}, SizeOf(metric), #0); | |||
| metric.AntiAliasing := tsAANormal; | |||
| metric.FaceName := String(aFace^.family_name); | |||
| metric.StyleName := String(aFace^.style_name); | |||
| LoadNames(aFace, metric); | |||
| prop.Size := aSize; | |||
| prop.AntiAliasing := aAntiAliasing; | |||
| prop.DefaultChar := '?'; | |||
| prop.Style := aStyle + [tsStyleBold, tsStyleItalic]; | |||
| metric.Size := aSize; | |||
| metric.AntiAliasing := aAntiAliasing; | |||
| metric.DefaultChar := '?'; | |||
| metric.Style := aStyle + [tsStyleBold, tsStyleItalic]; | |||
| if ((aFace^.style_flags and FT_STYLE_FLAG_BOLD) = 0) then | |||
| Exclude(prop.Style, tsStyleBold); | |||
| Exclude(metric.Style, tsStyleBold); | |||
| if ((aFace^.style_flags and FT_STYLE_FLAG_ITALIC) = 0) then | |||
| Exclude(prop.Style, tsStyleItalic); | |||
| Exclude(metric.Style, tsStyleItalic); | |||
| prop.Ascent := aFace^.size^.metrics.ascender div FT_SIZE_FACTOR; | |||
| prop.Descent := -aFace^.size^.metrics.descender div FT_SIZE_FACTOR; | |||
| prop.ExternalLeading := 0; | |||
| prop.BaseLineOffset := 0; | |||
| metric.Ascent := aFace^.size^.metrics.ascender div FT_SIZE_FACTOR; | |||
| metric.Descent := -aFace^.size^.metrics.descender div FT_SIZE_FACTOR; | |||
| metric.ExternalLeading := 0; | |||
| metric.BaseLineOffset := 0; | |||
| prop.UnderlinePos := aFace^.underline_position div FT_SIZE_FACTOR; | |||
| prop.UnderlineSize := aFace^.underline_thickness div FT_SIZE_FACTOR; | |||
| metric.UnderlinePos := aFace^.underline_position div FT_SIZE_FACTOR; | |||
| metric.UnderlineSize := aFace^.underline_thickness div FT_SIZE_FACTOR; | |||
| os2 := PTT_OS2(FT_Get_Sfnt_Table(aFace, FT_SFNT_OS2)); | |||
| if Assigned(os2) and (os2^.version <> $FFFF) then begin | |||
| prop.StrikeoutPos := os2^.yStrikeoutPosition div FT_SIZE_FACTOR; | |||
| prop.StrikeoutSize := os2^.yStrikeoutSize div FT_SIZE_FACTOR; | |||
| metric.StrikeoutPos := os2^.yStrikeoutPosition div FT_SIZE_FACTOR; | |||
| metric.StrikeoutSize := os2^.yStrikeoutSize div FT_SIZE_FACTOR; | |||
| end; | |||
| hz := PTT_HoriHeader(FT_Get_Sfnt_Table(aFace, FT_SFNT_HHEA)); | |||
| if Assigned(hz) then begin | |||
| prop.ExternalLeading := hz^.Line_Gap div FT_SIZE_FACTOR; | |||
| end; | |||
| result := TtsFontFreeType.Create(TtsFreeTypeFaceHandle.Create(aFace), aRenderer, self, prop); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsFontGeneratorFreeType.GetGlyphMetrics(const aFont: TtsFont; const aCharCode: WideChar; out aGlyphOrigin, aGlyphSize: TtsPosition; out aAdvance: Integer): Boolean; | |||
| var | |||
| font: TtsFontFreeType; | |||
| err: FT_Error; | |||
| begin | |||
| result := false; | |||
| aGlyphOrigin.x := 0; | |||
| aGlyphOrigin.x := 0; | |||
| aGlyphSize.x := 0; | |||
| aGlyphSize.y := 0; | |||
| aAdvance := 0; | |||
| font := ConvertFont(aFont); | |||
| case font.Properties.AntiAliasing of | |||
| tsAANormal: | |||
| err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT); | |||
| tsAANone: | |||
| err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME); | |||
| else | |||
| raise EtsException.Create('unknown anti aliasing'); | |||
| end; | |||
| case err of | |||
| FT_ERR_None: | |||
| { nop }; | |||
| FT_ERR_Invalid_Character_Code: | |||
| exit; | |||
| else | |||
| raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err)); | |||
| end; | |||
| result := true; | |||
| with font.fHandle.fFace^.glyph^.metrics do begin | |||
| aAdvance := horiAdvance div FT_SIZE_FACTOR; | |||
| aGlyphOrigin.x := horiBearingX div FT_SIZE_FACTOR; | |||
| aGlyphOrigin.y := horiBearingY div FT_SIZE_FACTOR; | |||
| aGlyphSize.x := width div FT_SIZE_FACTOR; | |||
| aGlyphSize.y := height div FT_SIZE_FACTOR; | |||
| metric.ExternalLeading := hz^.Line_Gap div FT_SIZE_FACTOR; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsFontGeneratorFreeType.GetCharImage(const aFont: TtsFont; const aCharCode: WideChar; const aCharImage: TtsImage); | |||
| var | |||
| font: TtsFontFreeType; | |||
| err: FT_Error; | |||
| g: FT_GlyphSlot; | |||
| b: PFT_Bitmap; | |||
| procedure CopyGray; | |||
| var | |||
| x, y: Integer; | |||
| src, dst: PByte; | |||
| c: TtsColor4f; | |||
| begin | |||
| aCharImage.CreateEmpty(font.Renderer.Format, b^.width, b^.rows); | |||
| c := tsColor4f(1, 1, 1, 1); | |||
| for y := 0 to b^.rows-1 do begin | |||
| src := b^.buffer + y * b^.pitch; | |||
| dst := aCharImage.Scanline[y]; | |||
| for x := 0 to b^.width-1 do begin | |||
| c.a := src^ / $FF; | |||
| inc(src, 1); | |||
| tsFormatMap(aCharImage.Format, dst, c); | |||
| end; | |||
| end; | |||
| end; | |||
| procedure CopyMono; | |||
| var | |||
| x, y, i, cnt: Integer; | |||
| src, dst: PByte; | |||
| tmp: Byte; | |||
| c: TtsColor4f; | |||
| begin | |||
| aCharImage.CreateEmpty(font.Renderer.Format, b^.width, b^.rows); | |||
| c := tsColor4f(1, 1, 1, 1); | |||
| for y := 0 to b^.rows-1 do begin | |||
| src := b^.buffer + y * b^.pitch; | |||
| dst := aCharImage.Scanline[y]; | |||
| x := b^.width; | |||
| while (x > 0) do begin | |||
| cnt := min(8, x); | |||
| tmp := src^; | |||
| inc(src, 1); | |||
| for i := 1 to cnt do begin | |||
| if ((tmp and $80) > 0) then | |||
| c.a := 1.0 | |||
| else | |||
| c.a := 0.0; | |||
| tmp := (tmp and not $80) shl 1; | |||
| tsFormatMap(aCharImage.Format, dst, c); | |||
| end; | |||
| dec(x, cnt); | |||
| end; | |||
| end; | |||
| end; | |||
| begin | |||
| font := ConvertFont(aFont); | |||
| g := font.fHandle.fFace^.glyph; | |||
| if not (font.Properties.AntiAliasing in [tsAANormal, tsAANone]) then | |||
| raise Exception.Create('unknown anti aliasing'); | |||
| case font.Properties.AntiAliasing of | |||
| tsAANormal: | |||
| err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_DEFAULT or FT_LOAD_RENDER); | |||
| tsAANone: | |||
| err := FT_Load_Char(font.fHandle.fFace, Ord(aCharCode), FT_LOAD_MONOCHROME or FT_LOAD_TARGET_MONO or FT_LOAD_RENDER); | |||
| end; | |||
| if (err <> 0) then | |||
| raise EtsException.Create('unable to set glyph metrix: error=' + IntToStr(err)); | |||
| if (g^.format <> FT_GLYPH_FORMAT_BITMAP) then | |||
| raise EtsException.Create('invalid glyph format'); | |||
| b := @g^.bitmap; | |||
| case b^.pixel_mode of | |||
| FT_PIXEL_MODE_MONO: | |||
| CopyMono; | |||
| FT_PIXEL_MODE_GRAY: | |||
| CopyGray; | |||
| else | |||
| raise EtsException.Create('unknown glyph bitmap format'); | |||
| end; | |||
| result := TtsFontFreeType.Create(TtsFreeTypeFaceHandle.Create(aFace), self, metric); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsFontGeneratorFreeType.GetFontByFile(const aFilename: String; const aRenderer: TtsRenderer; | |||
| const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| function TtsFontCreatorFreeType.GetFontByFile(const aFilename: String; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| var | |||
| face: FT_Face; | |||
| err: FT_Error; | |||
| @@ -363,20 +348,22 @@ begin | |||
| err := FT_New_Face(fHandle, PAnsiChar(aFilename), 0, @face); | |||
| if (err <> 0) then | |||
| raise EtsException.Create('unable to create free type face from file: ' + aFilename + ' error=' + IntToStr(err)); | |||
| result := CreateFont(face, aRenderer, aSize, aStyle, aAntiAliasing); | |||
| result := CreateFont(face, aSize, aStyle, aAntiAliasing); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsFontGeneratorFreeType.GetFontByStream(const aStream: TStream; const aRenderer: TtsRenderer; | |||
| const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| function TtsFontCreatorFreeType.GetFontByStream(const aStream: TStream; const aSize: Integer; const aStyle: TtsFontStyles; const aAntiAliasing: TtsAntiAliasing): TtsFont; | |||
| var | |||
| face: FT_Face; | |||
| err: FT_Error; | |||
| ms: TMemoryStream; | |||
| p: PBYte; | |||
| begin | |||
| if (aStream is TMemoryStream) then begin | |||
| ms := (aStream as TMemoryStream); | |||
| err := FT_New_Memory_Face(fHandle, PByte(ms.Memory) + ms.Position, ms.Size - ms.Position, 0, @face); | |||
| p := ms.Memory; | |||
| inc(p, ms.Position); | |||
| err := FT_New_Memory_Face(fHandle, p, ms.Size - ms.Position, 0, @face); | |||
| end else begin | |||
| ms := TMemoryStream.Create; | |||
| try | |||
| @@ -388,18 +375,18 @@ begin | |||
| end; | |||
| if (err <> 0) then | |||
| raise EtsException.Create('unable to create free type face from stream: error=' + IntToStr(err)); | |||
| result := CreateFont(face, aRenderer, aSize, aStyle, aAntiAliasing); | |||
| result := CreateFont(face, aSize, aStyle, aAntiAliasing); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsFontGeneratorFreeType.Create(const aContext: TtsContext); | |||
| constructor TtsFontCreatorFreeType.Create(const aContext: TtsContext); | |||
| begin | |||
| inherited Create(aContext); | |||
| fHandle := InitFreeType; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsFontGeneratorFreeType.Destroy; | |||
| destructor TtsFontCreatorFreeType.Destroy; | |||
| begin | |||
| inherited Destroy; // first call interited | |||
| QuitFreeType; // QuitFreeType will free callpacks | |||
| @@ -1,13 +1,14 @@ | |||
| unit utsFreeType; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode delphi}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, syncobjs, dynlibs, utsTextSuite; | |||
| Classes, SysUtils, syncobjs, {$IFDEF FPC}dynlibs,{$ELSE}windows,{$ENDIF} | |||
| utsUtils; | |||
| type | |||
| // Simple Types | |||
| @@ -598,6 +599,16 @@ procedure QuitFreeType; | |||
| implementation | |||
| {$IFNDEF FPC} | |||
| type | |||
| TLibHandle = HMODULE; | |||
| function GetLastOSError: Cardinal; | |||
| begin | |||
| result := GetLastError; | |||
| end; | |||
| {$ENDIF} | |||
| {$IFDEF WINDOWS} | |||
| {$IFDEF WIN32} | |||
| {$DEFINE TS_FT_WIN32} | |||
| @@ -631,7 +642,7 @@ function InitFreeType: FT_Library; | |||
| function GetProcAddr(const aName: String): Pointer; | |||
| begin | |||
| result := GetProcAddress(FreeTypeLibHandle, aName); | |||
| result := GetProcAddress(FreeTypeLibHandle, PAnsiChar(aName)); | |||
| if not Assigned(result) then | |||
| raise EtsException.Create('unable to load procedure from library: ' + aName); | |||
| end; | |||
| @@ -225,7 +225,7 @@ procedure QuitGDI; | |||
| implementation | |||
| uses | |||
| utsTextSuite; | |||
| utsUtils; | |||
| const | |||
| LIB_GDI32 = 'gdi32.dll'; | |||
| @@ -0,0 +1,437 @@ | |||
| unit utsImage; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTypes, utsUtils; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsImage = class; | |||
| TtsImageFunc = procedure(const aImage: TtsImage; X, Y: Integer; var aPixel: TtsColor4f; aArgs: Pointer); | |||
| TtsImage = class(TObject) | |||
| private | |||
| fWidth: Integer; | |||
| fHeight: Integer; | |||
| fDataSize: Integer; | |||
| fLineSize: Integer; | |||
| fFormat: TtsFormat; | |||
| fData: Pointer; | |||
| fHasScanlines: Boolean; | |||
| fScanlines: array of Pointer; | |||
| function GetScanline(const aIndex: Integer): Pointer; | |||
| function GetIsEmpty: Boolean; | |||
| procedure UpdateScanlines; | |||
| procedure SetData( | |||
| const aData: Pointer; | |||
| const aFormat: TtsFormat = tsFormatEmpty; | |||
| const aWidth: Integer = 0; | |||
| const aHeight: Integer = 0; | |||
| const aLineSize: Integer = 0; | |||
| const aDataSize: Integer = 0); | |||
| public | |||
| property IsEmpty: Boolean read GetIsEmpty; | |||
| property Width: Integer read fWidth; | |||
| property Height: Integer read fHeight; | |||
| property LineSize: Integer read fLineSize; | |||
| property DataSize: Integer read fDataSize; | |||
| property Format: TtsFormat read fFormat; | |||
| property Data: Pointer read fData; | |||
| property Scanline[const aIndex: Integer]: Pointer read GetScanline; | |||
| function GetPixelAt(const x, y: Integer; out aColor: TtsColor4f): Boolean; | |||
| procedure Assign(const aImage: TtsImage); | |||
| procedure CreateEmpty(const aFormat: TtsFormat; const aWidth, aHeight: Integer); | |||
| procedure LoadFromFunc(const aFunc: TtsImageFunc; const aArgs: Pointer); | |||
| procedure Resize(const aNewWidth, aNewHeight, X, Y: Integer); | |||
| procedure FindMinMax(out aRect: TtsRect); | |||
| procedure FillColor(const aColor: TtsColor4f; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes); | |||
| procedure FillPattern(const aPattern: TtsImage; X, Y: Integer; const aChannelMask: TtsColorChannels; const aModes: TtsImageModes); | |||
| procedure Blend(const aImage: TtsImage; const X, Y: Integer; const aFunc: TtsBlendColorFunc); | |||
| procedure Blur(const aHorzKernel, aVertKernel: TtsKernel1D; const aChannelMask: TtsColorChannels); | |||
| constructor Create; | |||
| destructor Destroy; override; | |||
| end; | |||
| implementation | |||
| uses | |||
| Math, | |||
| utsConstants; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsImage////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsImage.GetScanline(const aIndex: Integer): Pointer; | |||
| begin | |||
| if not fHasScanlines then | |||
| UpdateScanlines; | |||
| if fHasScanlines and (aIndex >= 0) and (aIndex <= High(fScanlines)) then | |||
| result := fScanlines[aIndex] | |||
| else | |||
| result := nil; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsImage.GetIsEmpty: Boolean; | |||
| begin | |||
| result := not Assigned(fData); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.UpdateScanlines; | |||
| var | |||
| i: Integer; | |||
| tmp: PByte; | |||
| begin | |||
| SetLength(fScanlines, fHeight); | |||
| for i := 0 to fHeight-1 do begin | |||
| tmp := fData; | |||
| inc(tmp, i * fLineSize); | |||
| fScanlines[i] := tmp; | |||
| end; | |||
| fHasScanlines := true; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.SetData(const aData: Pointer; const aFormat: TtsFormat; | |||
| const aWidth: Integer; const aHeight: Integer; | |||
| const aLineSize: Integer; const aDataSize: Integer); | |||
| begin | |||
| fHasScanlines := false; | |||
| if Assigned(fData) then | |||
| FreeMemory(fData); | |||
| fData := aData; | |||
| if Assigned(fData) then begin | |||
| fWidth := aWidth; | |||
| fHeight := aHeight; | |||
| fFormat := aFormat; | |||
| fLineSize := aLineSize; | |||
| fDataSize := aDataSize; | |||
| end else begin | |||
| fWidth := 0; | |||
| fHeight := 0; | |||
| fLineSize := 0; | |||
| fDataSize := 0; | |||
| fFormat := tsFormatEmpty; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsImage.GetPixelAt(const x, y: Integer; out aColor: TtsColor4f): Boolean; | |||
| var | |||
| p: PByte; | |||
| begin | |||
| result := (x >= 0) and (x < Width) and (y >= 0) and (y < Height); | |||
| if result then begin | |||
| p := Scanline[y]; | |||
| inc(p, x * tsFormatSize(Format)); | |||
| tsFormatUnmap(Format, p, aColor); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.Assign(const aImage: TtsImage); | |||
| var | |||
| ImgData: Pointer; | |||
| begin | |||
| GetMem(ImgData, aImage.DataSize); | |||
| if Assigned(ImgData) then | |||
| Move(aImage.Data^, ImgData^, aImage.DataSize); | |||
| SetData(ImgData, aImage.Format, aImage.Width, aImage.Height, aImage.LineSize, aImage.DataSize); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.CreateEmpty(const aFormat: TtsFormat; const aWidth, aHeight: Integer); | |||
| var | |||
| ImgData: PByte; | |||
| lSize, dSize: Integer; | |||
| begin | |||
| lSize := aWidth * tsFormatSize(aFormat); | |||
| lSize := lSize + ((4 - (lSize mod 4)) mod 4); | |||
| dSize := aHeight * lSize; | |||
| ImgData := AllocMem(dSize); | |||
| FillChar(ImgData^, dSize, #0); | |||
| SetData(ImgData, aFormat, aWidth, aHeight, lSize, dSize); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.LoadFromFunc(const aFunc: TtsImageFunc; const aArgs: Pointer); | |||
| var | |||
| X, Y: Integer; | |||
| c: TtsColor4f; | |||
| p, tmp: PByte; | |||
| begin | |||
| for Y := 0 to Height - 1 do begin | |||
| p := ScanLine[Y]; | |||
| for X := 0 to Width - 1 do begin | |||
| tmp := p; | |||
| tsFormatUnmap(fFormat, tmp, c); | |||
| aFunc(Self, X, Y, c, aArgs); | |||
| tsFormatMap(fFormat, p, c); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.Resize(const aNewWidth, aNewHeight, X, Y: Integer); | |||
| var | |||
| ImgData: PByte; | |||
| pSize, lSize, dSize: Integer; | |||
| src, dst: PByte; | |||
| YStart, YEnd, YPos, XStart, XEnd: Integer; | |||
| begin | |||
| if (aNewHeight = 0) or (aNewWidth = 0) then begin | |||
| SetData(nil); | |||
| exit; | |||
| end; | |||
| pSize := tsFormatSize(Format); | |||
| lSize := pSize * aNewWidth; | |||
| lSize := lSize + ((4 - (lSize mod 4)) mod 4); | |||
| dSize := lSize * aNewHeight; | |||
| GetMem(ImgData, dSize); | |||
| try | |||
| FillChar(ImgData^, dSize, 0); | |||
| // positions | |||
| YStart := Max(0, Y); | |||
| YEnd := Min(aNewHeight, Y + Height); | |||
| XStart := Max(0, X); | |||
| XEnd := Min(aNewWidth, X + Width); | |||
| // copy data | |||
| for YPos := YStart to YEnd -1 do begin | |||
| dst := ImgData; | |||
| Inc(dst, lSize * YPos + pSize * XStart); | |||
| src := fData; | |||
| Inc(src, fLineSize * (YPos - Y) + pSize * (XStart - X)); | |||
| Move(src^, dst^, (XEnd - XStart) * pSize); | |||
| end; | |||
| // assign | |||
| SetData(ImgData, Format, aNewWidth, aNewHeight, lSize, dSize); | |||
| except | |||
| FreeMem(ImgData); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.FindMinMax(out aRect: TtsRect); | |||
| var | |||
| X, Y: Integer; | |||
| c: TtsColor4f; | |||
| p: PByte; | |||
| begin | |||
| aRect.Top := -1; | |||
| aRect.Left := -1; | |||
| aRect.Right := -1; | |||
| aRect.Bottom := -1; | |||
| // Search for MinMax | |||
| for Y := 0 to Height-1 do begin | |||
| p := ScanLine[Y]; | |||
| for X := 0 to Width-1 do begin | |||
| tsFormatUnmap(Format, p, c); | |||
| if c.a > 0 then begin | |||
| if (X < aRect.Left) or (aRect.Left = -1) then | |||
| aRect.Left := X; | |||
| if (X+1 > aRect.Right) or (aRect.Right = -1) then | |||
| aRect.Right := X+1; | |||
| if (Y < aRect.Top) or (aRect.Top = -1) then | |||
| aRect.Top := Y; | |||
| if (Y+1 > aRect.Bottom) or (aRect.Bottom = -1) then | |||
| aRect.Bottom := Y+1; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.FillColor(const aColor: TtsColor4f; const aChannelMask: TtsColorChannels; | |||
| const aModes: TtsImageModes); | |||
| var | |||
| x, y: Integer; | |||
| rp, wp: PByte; | |||
| c: TtsColor4f; | |||
| ch: TtsColorChannel; | |||
| i: Integer; | |||
| begin | |||
| for y := 0 to Height-1 do begin | |||
| rp := Scanline[y]; | |||
| wp := rp; | |||
| for x := 0 to Width-1 do begin | |||
| tsFormatUnmap(Format, rp, c); | |||
| for i := 0 to 3 do begin | |||
| ch := TtsColorChannel(i); | |||
| if (ch in aChannelMask) then | |||
| c.arr[i] := TS_IMAGE_MODE_FUNCTIONS[aModes[ch]](aColor.arr[i], c.arr[i]); | |||
| end; | |||
| tsFormatMap(Format, wp, c); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.FillPattern(const aPattern: TtsImage; X, Y: Integer; | |||
| const aChannelMask: TtsColorChannels; const aModes: TtsImageModes); | |||
| var | |||
| _x, _y, posX, i: Integer; | |||
| src, dst, tmp: PByte; | |||
| cSrc, cDst: TtsColor4f; | |||
| ch: TtsColorChannel; | |||
| begin | |||
| if x < 0 then | |||
| x := Random(aPattern.Width); | |||
| if y < 0 then | |||
| y := Random(aPattern.Height); | |||
| for _y := 0 to Height-1 do begin | |||
| src := aPattern.Scanline[(y + _y) mod aPattern.Height]; | |||
| dst := Scanline[_y]; | |||
| inc(src, x); | |||
| posX := x; | |||
| for _x := 0 to Width-1 do begin | |||
| if (posX >= aPattern.Width) then begin | |||
| src := aPattern.Scanline[(y + _y) mod aPattern.Height]; | |||
| posX := 0; | |||
| end; | |||
| tmp := dst; | |||
| tsFormatUnmap(aPattern.Format, src, cSrc); | |||
| tsFormatUnmap(Format, tmp, cDst); | |||
| for i := 0 to 3 do begin | |||
| ch := TtsColorChannel(i); | |||
| if (ch in aChannelMask) then | |||
| cDst.arr[i] := TS_IMAGE_MODE_FUNCTIONS[aModes[ch]](cSrc.arr[i], cDst.arr[i]); | |||
| end; | |||
| tsFormatMap(Format, dst, cDst); | |||
| inc(posX); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.Blend(const aImage: TtsImage; const X, Y: Integer; const aFunc: TtsBlendColorFunc); | |||
| var | |||
| _x, _y, x1, x2, y1, y2: Integer; | |||
| src, dst, tmp: PByte; | |||
| srcColor, dstColor: TtsColor4f; | |||
| srcPixelSize, dstPixelSize: Integer; | |||
| begin | |||
| x1 := Max(X, 0); | |||
| x2 := Min(X + aImage.Width , Width); | |||
| y1 := Max(Y, 0); | |||
| y2 := Min(Y + aImage.Height, Height); | |||
| srcPixelSize := tsFormatSize(aImage.Format); | |||
| dstPixelSize := tsFormatSize(Format); | |||
| for _y := y1 to y2-1 do begin | |||
| src := aImage.Scanline[_y - min(y1, y)]; | |||
| dst := Scanline[_y]; | |||
| inc(src, (x1 - x) * srcPixelSize); | |||
| inc(dst, x1 * dstPixelSize); | |||
| tmp := dst; | |||
| for _x := x1 to x2-1 do begin | |||
| tsFormatUnmap(aImage.Format, src, srcColor); | |||
| tsFormatUnmap( Format, dst, dstColor); | |||
| tsFormatMap(aImage.Format, tmp, aFunc(srcColor, dstColor)); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsImage.Blur(const aHorzKernel, aVertKernel: TtsKernel1D; const aChannelMask: TtsColorChannels); | |||
| var | |||
| tmpImage: TtsImage; | |||
| procedure DoBlur(const aSrc, aDst: TtsImage; const aKernel: TtsKernel1D; const ShiftX, ShiftY: Integer); | |||
| var | |||
| x, y, i, j: Integer; | |||
| src, dst: PByte; | |||
| v: Single; | |||
| c, tmp: TtsColor4f; | |||
| begin | |||
| for y := 0 to Height-1 do begin | |||
| src := aSrc.Scanline[y]; | |||
| dst := aDst.Scanline[y]; | |||
| for x := 0 to Width-1 do begin | |||
| // read color and clear channels | |||
| v := 0; | |||
| tsFormatUnmap(aSrc.Format, src, c); | |||
| for i := 0 to 3 do | |||
| if (TtsColorChannel(i) in aChannelMask) then | |||
| c.arr[i] := 0; | |||
| // do blur | |||
| for i := 0 to aKernel.ItemCount-1 do with aKernel.Items[i] do begin | |||
| if aSrc.GetPixelAt(x + Offset * ShiftX, y + Offset * ShiftY, tmp) then begin | |||
| for j := 0 to 3 do begin | |||
| if (TtsColorChannel(j) in aChannelMask) then | |||
| c.arr[j] := c.arr[j] + tmp.arr[j] * Value; | |||
| end; | |||
| v := v + Value; | |||
| end; | |||
| end; | |||
| // calc final color and write | |||
| for i := 0 to 3 do | |||
| if (TtsColorChannel(i) in aChannelMask) then | |||
| c.arr[i] := c.arr[i] / v; | |||
| tsFormatMap(aDst.Format, dst, c); | |||
| end; | |||
| end; | |||
| end; | |||
| begin | |||
| tmpImage := TtsImage.Create; | |||
| try | |||
| tmpImage.CreateEmpty(Format, Width, Height); | |||
| tmpImage.FillColor(tsColor4f(1, 1, 1, 0), TS_COLOR_CHANNELS_RGBA, TS_IMAGE_MODES_REPLACE_ALL); | |||
| DoBlur(self, tmpImage, aHorzKernel, 1, 0); | |||
| DoBlur(tmpImage, self, aVertKernel, 0, 1); | |||
| finally | |||
| FreeAndNil(tmpImage); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsImage.Create; | |||
| begin | |||
| inherited Create; | |||
| SetData(nil); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsImage.Destroy; | |||
| begin | |||
| SetData(nil); | |||
| inherited Destroy; | |||
| end; | |||
| end. | |||
| @@ -1,25 +1,27 @@ | |||
| unit utsOpenGLUtils; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTextSuite, utsTypes; | |||
| utsTypes, utsRenderer, utsImage, utsContext, utsUtils, utsChar; | |||
| type | |||
| TtsCharRenderRefOpenGL = class(TtsCharRenderRef) | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsOpenGLRenderRef = class(TObject) | |||
| public | |||
| TextureID: Integer; // ID of OpenGL texture where the char is stored in | |||
| TextureID: Integer; | |||
| Size: TtsPosition; | |||
| TexMat: TtsMatrix4f; | |||
| VertMat: TtsMatrix4f; | |||
| constructor Create; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| PtsTextureUsageItem = ^TtsTextureUsageItem; | |||
| TtsTextureUsageItem = packed record | |||
| children: array[0..3] of PtsTextureUsageItem; | |||
| @@ -29,9 +31,10 @@ type | |||
| TtsTextureTreeItem = packed record | |||
| value: SmallInt; | |||
| children: array[0..1] of PtsTextureTreeItem; | |||
| ref: TtsCharRenderRefOpenGL; | |||
| ref: TtsOpenGLRenderRef; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| PtsFontTexture = ^TtsFontTexture; | |||
| TtsFontTexture = packed record | |||
| ID: Integer; // OpenGL texture ID | |||
| @@ -42,6 +45,7 @@ type | |||
| Count: Integer; // number of chars stored in this texture | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsBaseOpenGL = class(TtsRenderer) | |||
| private | |||
| fTextureSize: Integer; | |||
| @@ -61,17 +65,16 @@ type | |||
| function CreateNewTexture: PtsFontTexture; virtual; | |||
| procedure FreeTexture(var aTexture: PtsFontTexture); virtual; | |||
| procedure UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer); virtual; | |||
| procedure UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, Y: Integer); virtual; | |||
| public | |||
| function CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef; override; | |||
| procedure FreeRenderRef(const aRenderRef: TtsRenderRef); override; | |||
| 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; | |||
| procedure SetDrawPos(const aValue: TtsPosition); override; | |||
| function GetDrawPos: TtsPosition; override; | |||
| procedure MoveDrawPos(const aOffset: TtsPosition); override; | |||
| procedure SetColor(const aValue: TtsColor4f); override; | |||
| public | |||
| property TextureSize: Integer read fTextureSize write fTextureSize; | |||
| @@ -84,9 +87,9 @@ type | |||
| implementation | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsCharRenderRefOpenGL//////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsOpenGLRenderRef//////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsCharRenderRefOpenGL.Create; | |||
| constructor TtsOpenGLRenderRef.Create; | |||
| begin | |||
| inherited Create; | |||
| TextureID := 0; | |||
| @@ -149,13 +152,14 @@ begin | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsBaseOpenGL.UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer); | |||
| procedure TtsBaseOpenGL.UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, | |||
| Y: Integer); | |||
| begin | |||
| // DUMMY | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; | |||
| function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef; | |||
| var | |||
| GlyphWidth, GlyphHeight: Integer; | |||
| @@ -199,7 +203,7 @@ var | |||
| end; | |||
| end; | |||
| function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL; | |||
| function AddToTexture(const aTexture: PtsFontTexture): TtsOpenGLRenderRef; | |||
| var | |||
| x, y, wChar, hChar, l, t: Integer; | |||
| item: PtsTextureTreeItem; | |||
| @@ -208,15 +212,15 @@ var | |||
| if not Assigned(item) then | |||
| raise EtsRendererOpenGL.Create('unable to add glyph to texture'); | |||
| item^.ref := TtsCharRenderRefOpenGL.Create; | |||
| item^.ref := TtsOpenGLRenderRef.Create; | |||
| result := item^.ref; | |||
| wChar := aChar.GlyphRect.Right - aChar.GlyphRect.Left; | |||
| hChar := aChar.GlyphRect.Bottom - aChar.GlyphRect.Top; | |||
| l := aChar.GlyphRect.Left + x; | |||
| t := aChar.GlyphRect.Top + y; | |||
| wChar := aChar.GlyphMetric.GlyphRect.Right - aChar.GlyphMetric.GlyphRect.Left; | |||
| hChar := aChar.GlyphMetric.GlyphRect.Bottom - aChar.GlyphMetric.GlyphRect.Top; | |||
| l := aChar.GlyphMetric.GlyphRect.Left + x; | |||
| t := aChar.GlyphMetric.GlyphRect.Top + y; | |||
| result.TextureID := aTexture^.ID; | |||
| result.Size := tsPosition(aCharImage.Width, aCharImage.Height); | |||
| result.Size := tsPosition(aImage.Width, aImage.Height); | |||
| result.TexMat := tsMatrix4f( | |||
| tsVector4f(wChar / aTexture^.Size, 0.0, 0.0, 0.0), | |||
| tsVector4f(0.0, hChar / aTexture^.Size, 0.0, 0.0), | |||
| @@ -226,20 +230,20 @@ var | |||
| tsVector4f(wChar, 0.0, 0.0, 0.0), | |||
| tsVector4f(0.0, hChar, 0.0, 0.0), | |||
| tsVector4f(0.0, 0.0, 1.0, 0.0), | |||
| tsVector4f(aChar.GlyphOrigin.x, -aChar.GlyphOrigin.y, 0.0, 1.0)); | |||
| tsVector4f(aChar.GlyphMetric.GlyphOrigin.x, -aChar.GlyphMetric.GlyphOrigin.y, 0.0, 1.0)); | |||
| UploadTexData(result, aCharImage, x, y); | |||
| UploadTexData(result, aImage, x, y); | |||
| end; | |||
| var | |||
| tex: PtsFontTexture; | |||
| begin | |||
| result := nil; | |||
| if aCharImage.IsEmpty then | |||
| if aImage.IsEmpty then | |||
| exit; | |||
| GlyphWidth := aCharImage.Width + 1; | |||
| GlyphHeight := aCharImage.Height + 1; | |||
| GlyphWidth := aImage.Width + 1; | |||
| GlyphHeight := aImage.Height + 1; | |||
| // try to add to existing texture | |||
| tex := fFirstTexture; | |||
| @@ -250,7 +254,7 @@ begin | |||
| // create new texture | |||
| if not Assigned(result) then begin | |||
| if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then | |||
| if (aImage.Width > TextureSize) or (aImage.Height > TextureSize) then | |||
| raise EtsRendererOpenGL.Create('char is to large to fit into a texture: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')'); | |||
| tex := CreateNewTexture; | |||
| result := AddToTexture(tex); | |||
| @@ -261,9 +265,9 @@ begin | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsBaseOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef); | |||
| procedure TtsBaseOpenGL.FreeRenderRef(const aRenderRef: TtsRenderRef); | |||
| var | |||
| ref: TtsCharRenderRefOpenGL; | |||
| ref: TtsOpenGLRenderRef; | |||
| tex: PtsFontTexture; | |||
| function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean; | |||
| @@ -310,10 +314,11 @@ var | |||
| end; | |||
| begin | |||
| ref := nil; | |||
| try | |||
| if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then | |||
| if not Assigned(aRenderRef) then | |||
| exit; | |||
| ref := (aCharRef as TtsCharRenderRefOpenGL); | |||
| ref := TtsOpenGLRenderRef(aRenderRef); | |||
| tex := fFirstTexture; | |||
| while Assigned(tex) do begin | |||
| if (tex^.ID = ref.TextureID) then begin | |||
| @@ -329,24 +334,22 @@ begin | |||
| tex := tex^.Next; | |||
| end; | |||
| finally | |||
| if Assigned(aCharRef) then | |||
| aCharRef.Free; | |||
| if Assigned(ref) then | |||
| ref.Free; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsBaseOpenGL.BeginRender; | |||
| begin | |||
| inherited BeginRender; | |||
| fRenderPos.x := 0; | |||
| fRenderPos.y := 0; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsBaseOpenGL.SetDrawPos(const X, Y: Integer); | |||
| procedure TtsBaseOpenGL.SetDrawPos(const aValue: TtsPosition); | |||
| begin | |||
| fRenderPos.x := X; | |||
| fRenderPos.y := Y; | |||
| fRenderPos := aValue; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| @@ -356,16 +359,16 @@ begin | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsBaseOpenGL.MoveDrawPos(const X, Y: Integer); | |||
| procedure TtsBaseOpenGL.MoveDrawPos(const aOffset: TtsPosition); | |||
| begin | |||
| fRenderPos.x := fRenderPos.x + X; | |||
| fRenderPos.y := fRenderPos.y + Y; | |||
| fRenderPos.x := fRenderPos.x + aOffset.x; | |||
| fRenderPos.y := fRenderPos.y + aOffset.y; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsBaseOpenGL.SetColor(const aColor: TtsColor4f); | |||
| procedure TtsBaseOpenGL.SetColor(const aValue: TtsColor4f); | |||
| begin | |||
| fColor := aColor; | |||
| fColor := aValue; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| @@ -379,6 +382,7 @@ begin | |||
| fRenderPos := tsPosition(0, 0); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsBaseOpenGL.Destroy; | |||
| begin | |||
| FreeTextures(fFirstTexture); | |||
| @@ -1,277 +0,0 @@ | |||
| unit utsPostProcess; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, utsTextSuite, utsTypes; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessFillColor = class(TtsPostProcessStep) | |||
| private | |||
| fColor: TtsColor4f; | |||
| fModes: TtsImageModes; | |||
| fChannels: TtsColorChannels; | |||
| protected | |||
| procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override; | |||
| public | |||
| constructor Create(const aColor: TtsColor4f; | |||
| const aModes: TtsImageModes; const aChannels: TtsColorChannels); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessFillPattern = class(TtsPostProcessStep) | |||
| private | |||
| fPattern: TtsImage; | |||
| fOwnsPattern: Boolean; | |||
| fX, fY: Integer; | |||
| fModes: TtsImageModes; | |||
| fChannels: TtsColorChannels; | |||
| protected | |||
| procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override; | |||
| public | |||
| constructor Create(const aPattern: TtsImage; const aOwnsPattern: Boolean; const X, Y: Integer; | |||
| const aModes: TtsImageModes; const aChannels: TtsColorChannels); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessBorder = class(TtsPostProcessStep) | |||
| private | |||
| fKernel: TtsKernel2D; | |||
| fColor: TtsColor4f; | |||
| fKeepCharSize: Boolean; | |||
| public | |||
| procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override; | |||
| public | |||
| constructor Create(const aWidth, aStrength: Single; const aColor: TtsColor4f; | |||
| const aKeepCharSize: Boolean = false); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessShadow = class(TtsPostProcessStep) | |||
| private | |||
| fKernel: TtsKernel1D; | |||
| fColor: TtsColor4f; | |||
| fX, fY: Integer; | |||
| protected | |||
| procedure Execute(const aChar: TtsChar; const aCharImage: TtsImage); override; | |||
| public | |||
| constructor Create(const aRadius, aStrength: Single; const X, Y: Integer; const aColor: TtsColor4f); | |||
| destructor Destroy; override; | |||
| end; | |||
| implementation | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessFillColor/////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessFillColor.Execute(const aChar: TtsChar; const aCharImage: TtsImage); | |||
| begin | |||
| if Assigned(aCharImage) then | |||
| aCharImage.FillColor(fColor, fChannels, fModes); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessFillColor.Create(const aColor: TtsColor4f; const aModes: TtsImageModes; const aChannels: TtsColorChannels); | |||
| begin | |||
| inherited Create; | |||
| fColor := aColor; | |||
| fModes := aModes; | |||
| fChannels := aChannels; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessFillPattern///////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessFillPattern.Execute(const aChar: TtsChar; const aCharImage: TtsImage); | |||
| begin | |||
| if Assigned(aCharImage) then | |||
| aCharImage.FillPattern(fPattern, fX, fY, fChannels, fModes); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessFillPattern.Create(const aPattern: TtsImage; const aOwnsPattern: Boolean; const X, | |||
| Y: Integer; const aModes: TtsImageModes; const aChannels: TtsColorChannels); | |||
| begin | |||
| inherited Create; | |||
| fPattern := aPattern; | |||
| fOwnsPattern := aOwnsPattern; | |||
| fX := X; | |||
| fY := Y; | |||
| fModes := aModes; | |||
| fChannels := aChannels; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessFillPattern.Destroy; | |||
| begin | |||
| if fOwnsPattern then | |||
| FreeAndNil(fPattern); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessBorder////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessBorder.Execute(const aChar: TtsChar; const aCharImage: TtsImage); | |||
| var | |||
| orig: TtsImage; | |||
| x, y: Integer; | |||
| dst: PByte; | |||
| function BorderLookup: TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| c: TtsColor4f; | |||
| s: Single; | |||
| chan: TtsColorChannel; | |||
| mask: TtsColorChannels; | |||
| tmpX, tmpY: Integer; | |||
| begin | |||
| mask := TS_CHANNELS_RGBA; | |||
| result := tsColor4f(0, 0, 0, 0); | |||
| for i := 0 to fKernel.ItemCount-1 do begin | |||
| tmpX := x + fKernel.Items[i].OffsetX; | |||
| tmpY := y + fKernel.Items[i].OffsetY; | |||
| if (tmpX >= 0) and (tmpX < orig.Width) and | |||
| (tmpY >= 0) and (tmpY < orig.Height) and | |||
| orig.GetPixelAt(tmpX, tmpY, c) then | |||
| begin | |||
| {$IFDEF FPC} | |||
| for chan in mask do begin | |||
| {$ELSE} | |||
| for chan := low(TtsColorChannel) to high(TtsColorChannel) do if (chan in mask) then begin | |||
| {$ENDIF} | |||
| s := c.arr[Integer(chan)] * fColor.arr[Integer(chan)] * fKernel.Items[i].Value; | |||
| if (s > result.arr[Integer(chan)]) then begin | |||
| result.arr[Integer(chan)] := s; | |||
| if (s >= 1.0) then begin | |||
| Exclude(mask, chan); | |||
| if (mask = []) then | |||
| exit; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| begin | |||
| if not Assigned(aCharImage) then | |||
| exit; | |||
| aCharImage.Resize( | |||
| aCharImage.Width + 2 * fKernel.SizeX, | |||
| aCharImage.Height + 2 * fKernel.SizeY, | |||
| fKernel.SizeX, fKernel.SizeY); | |||
| orig := TtsImage.Create; | |||
| try | |||
| orig.Assign(aCharImage); | |||
| aCharImage.FillColor(fColor, TS_CHANNELS_RGBA, TS_MODES_REPLACE_ALL); | |||
| for y := 0 to orig.Height-1 do begin | |||
| dst := aCharImage.Scanline[y]; | |||
| for x := 0 to orig.Width-1 do | |||
| tsFormatMap(aCharImage.Format, dst, BorderLookup); | |||
| end; | |||
| aCharImage.Blend(orig, 0, 0, @tsBlendFundAdditiveAlpha); | |||
| finally | |||
| FreeAndNil(orig); | |||
| end; | |||
| aChar.GlyphRect := tsRect( | |||
| aChar.GlyphRect.Left, | |||
| aChar.GlyphRect.Top, | |||
| aChar.GlyphRect.Right + 2 * fKernel.SizeX, | |||
| aChar.GlyphRect.Bottom + 2 * fKernel.SizeY); | |||
| if fKeepCharSize then begin | |||
| aChar.GlyphOrigin := tsPosition( | |||
| aChar.GlyphOrigin.x - fKernel.SizeX, | |||
| aChar.GlyphOrigin.y + fKernel.SizeY); | |||
| end else begin | |||
| aChar.Advance := aChar.Advance + 2 * fKernel.SizeX; | |||
| aChar.GlyphOrigin := tsPosition( | |||
| aChar.GlyphOrigin.x, | |||
| aChar.GlyphOrigin.y + fKernel.SizeY); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessBorder.Create(const aWidth, aStrength: Single; const aColor: TtsColor4f; const aKeepCharSize: Boolean); | |||
| begin | |||
| inherited Create; | |||
| fKernel := TtsKernel2D.Create(aWidth, aStrength); | |||
| fColor := aColor; | |||
| fKeepCharSize := aKeepCharSize; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessBorder.Destroy; | |||
| begin | |||
| FreeAndNil(fKernel); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessShadow////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessShadow.Execute(const aChar: TtsChar; const aCharImage: TtsImage); | |||
| var | |||
| orig: TtsImage; | |||
| tmpX, tmpY: Integer; | |||
| begin | |||
| orig := TtsImage.Create; | |||
| try | |||
| orig.Assign(aCharImage); | |||
| aCharImage.Resize( | |||
| aCharImage.Width + 2 * fKernel.Size, | |||
| aCharImage.Height + 2 * fKernel.Size, | |||
| fKernel.Size, fKernel.Size); | |||
| aCharImage.FillColor(fColor, TS_CHANNELS_RGBA, TS_MODES_MODULATE_ALPHA); | |||
| aCharImage.Blur(fKernel, fKernel, [tsChannelAlpha]); | |||
| tmpX := fKernel.Size - fX; | |||
| tmpY := fKernel.Size - fY; | |||
| aCharImage.Blend(orig, tmpX, tmpY, @tsBlendFundAlpha); | |||
| aChar.GlyphRect := tsRect( | |||
| aChar.GlyphRect.Left, | |||
| aChar.GlyphRect.Top, | |||
| aChar.GlyphRect.Right + 2 * fKernel.Size, | |||
| aChar.GlyphRect.Bottom + 2 * fKernel.Size); | |||
| aChar.GlyphOrigin := tsPosition( | |||
| aChar.GlyphOrigin.x - tmpX, | |||
| aChar.GlyphOrigin.y + tmpX); | |||
| finally | |||
| FreeAndNil(orig); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessShadow.Create(const aRadius, aStrength: Single; const X, Y: Integer; const aColor: TtsColor4f); | |||
| begin | |||
| inherited Create; | |||
| fKernel := TtsKernel1D.Create(aRadius, aStrength); | |||
| fX := X; | |||
| fY := Y; | |||
| fColor := aColor; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessShadow.Destroy; | |||
| begin | |||
| FreeAndNil(fKernel); | |||
| inherited Destroy; | |||
| end; | |||
| end. | |||
| @@ -0,0 +1,602 @@ | |||
| unit utsPostProcessor; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, contnrs, | |||
| utsUtils, utsChar, utsImage, utsContext, utsTypes; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsCharRange = record | |||
| Start: WideChar; | |||
| Stop: WideChar; | |||
| end; | |||
| PtsCharRange = ^TtsCharRange; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsCharRangeUsage = ( | |||
| tsUsageInclude, | |||
| tsUsageExclude); | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessor = class(TtsRefManager) | |||
| private | |||
| fContext: TtsContext; | |||
| fIncludeCharRanges: TList; | |||
| fExcludeCharRanges: TList; | |||
| procedure ClearList(const aList: TList); | |||
| public | |||
| property Context: TtsContext read fContext; | |||
| function IsInRange(const aCharCode: WideChar): Boolean; | |||
| procedure AddRange(const aUsage: TtsCharRangeUsage; const aStart, aStop: WideChar); | |||
| procedure AddChars(const aUsage: TtsCharRangeUsage; aChars: PWideChar); | |||
| procedure ClearRanges; | |||
| function Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; virtual; | |||
| constructor Create(const aContext: TtsContext); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessorList = class(TtsPostProcessor) | |||
| private | |||
| fItems: TObjectList; | |||
| function GetCount: Integer; | |||
| function GetOwnsObjects: Boolean; | |||
| procedure SetOwnsObjects(const aValue: Boolean); | |||
| public | |||
| property Count: Integer read GetCount; | |||
| property OwnsObjects: Boolean read GetOwnsObjects write SetOwnsObjects; | |||
| procedure Add(const aPostProcessor: TtsPostProcessor); | |||
| procedure Delete(const aIndex: Integer); | |||
| procedure Clear; | |||
| function Remove(const aPostProcessor: TtsPostProcessor): Integer; | |||
| function IndexOf(const aPostProcessor: TtsPostProcessor): Integer; | |||
| function Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; override; | |||
| constructor Create(const aContext: TtsContext; const aOwnsObjects: Boolean); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessorFillColor = class(TtsPostProcessor) | |||
| private | |||
| fColor: TtsColor4f; | |||
| fModes: TtsImageModes; | |||
| fChannels: TtsColorChannels; | |||
| public | |||
| property Color: TtsColor4f read fColor write fColor; | |||
| property Modes: TtsImageModes read fModes write fModes; | |||
| property Channels: TtsColorChannels read fChannels write fChannels; | |||
| function Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; override; | |||
| constructor Create( | |||
| const aContext: TtsContext; | |||
| const aColor: TtsColor4f; | |||
| const aModes: TtsImageModes; | |||
| const aChannels: TtsColorChannels); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessorFillPattern = class(TtsPostProcessor) | |||
| private | |||
| fPattern: TtsImage; | |||
| fOwnsPattern: Boolean; | |||
| fPosition: TtsPosition; | |||
| fModes: TtsImageModes; | |||
| fChannels: TtsColorChannels; | |||
| procedure SetPattern(aValue: TtsImage); | |||
| public | |||
| property Pattern: TtsImage read fPattern write SetPattern; | |||
| property OwnsPattern: Boolean read fOwnsPattern write fOwnsPattern; | |||
| property Position: TtsPosition read fPosition write fPosition; | |||
| property Modes: TtsImageModes read fModes write fModes; | |||
| property Channels: TtsColorChannels read fChannels write fChannels; | |||
| function Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; override; | |||
| constructor Create( | |||
| const aContext: TtsContext; | |||
| const aPattern: TtsImage; | |||
| const aOwnsPattern: Boolean; | |||
| const aPosition: TtsPosition; | |||
| const aModes: TtsImageModes; | |||
| const aChannels: TtsColorChannels); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessorBorder = class(TtsPostProcessor) | |||
| private | |||
| fKernel: TtsKernel2D; | |||
| fKeepSize: Boolean; | |||
| fColor: TtsColor4f; | |||
| fStrength: Single; | |||
| fWidth: Single; | |||
| procedure SetStrength(const aValue: Single); | |||
| procedure SetWidth(const aValue: Single); | |||
| public | |||
| property Width: Single read fWidth write SetWidth; | |||
| property Strength: Single read fStrength write SetStrength; | |||
| property Color: TtsColor4f read fColor write fColor; | |||
| property KeepSize: Boolean read fKeepSize write fKeepSize; | |||
| function Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; override; | |||
| constructor Create( | |||
| const aContext: TtsContext; | |||
| const aWidth, aStrength: Single; | |||
| const aColor: TtsColor4f; | |||
| const aKeepSize: Boolean); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsPostProcessorShadow = class(TtsPostProcessor) | |||
| private | |||
| fKernel: TtsKernel1D; | |||
| fColor: TtsColor4f; | |||
| fOffset: TtsPosition; | |||
| fRadius: Single; | |||
| fStrength: Single; | |||
| procedure SetRadius(const aValue: Single); | |||
| procedure SetStrength(const aValue: Single); | |||
| public | |||
| property Radius: Single read fRadius write SetRadius; | |||
| property Strength: Single read fStrength write SetStrength; | |||
| property Offset: TtsPosition read fOffset write fOffset; | |||
| property Color: TtsColor4f read fColor write fColor; | |||
| function Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; override; | |||
| constructor Create( | |||
| const aContext: TtsContext; | |||
| const aRadius, aStrength: Single; | |||
| const aOffset: TtsPosition; | |||
| const aColor: TtsColor4f); | |||
| destructor Destroy; override; | |||
| end; | |||
| implementation | |||
| uses | |||
| utsConstants; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessor////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessor.ClearList(const aList: TList); | |||
| var | |||
| i: Integer; | |||
| p: PtsCharRange; | |||
| begin | |||
| for i := 0 to aList.Count-1 do begin | |||
| p := aList[i]; | |||
| Dispose(p); | |||
| end; | |||
| aList.Clear; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessor.IsInRange(const aCharCode: WideChar): Boolean; | |||
| var | |||
| i: Integer; | |||
| p: PtsCharRange; | |||
| begin | |||
| result := (fIncludeCharRanges.Count = 0); | |||
| if not result then for i := 0 to fIncludeCharRanges.Count-1 do begin | |||
| p := fIncludeCharRanges[i]; | |||
| if (aCharCode >= p^.Start) and (aCharCode <= p^.Stop) then begin | |||
| result := true; | |||
| break; | |||
| end; | |||
| end; | |||
| if result then for i := 0 to fExcludeCharRanges.Count-1 do begin | |||
| p := fExcludeCharRanges[i]; | |||
| if (aCharCode >= p^.Start) and (aCharCode <= p^.Stop) then begin | |||
| result := false; | |||
| break; | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessor.AddRange(const aUsage: TtsCharRangeUsage; const aStart, aStop: WideChar); | |||
| var | |||
| p: PtsCharRange; | |||
| begin | |||
| new(p); | |||
| p^.Start := aStart; | |||
| p^.Stop := aStop; | |||
| case aUsage of | |||
| tsUsageInclude: fIncludeCharRanges.Add(p); | |||
| tsUsageExclude: fExcludeCharRanges.Add(p); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessor.AddChars(const aUsage: TtsCharRangeUsage; aChars: PWideChar); | |||
| begin | |||
| if not Assigned(aChars) then | |||
| exit; | |||
| while (aChars^ <> #0) do begin | |||
| AddRange(aUsage, aChars^, aChars^); | |||
| inc(aChars); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessor.ClearRanges; | |||
| begin | |||
| ClearList(fIncludeCharRanges); | |||
| ClearList(fExcludeCharRanges); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessor.Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; | |||
| begin | |||
| result := IsInRange(aChar.CharCode); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessor.Create(const aContext: TtsContext); | |||
| begin | |||
| inherited Create(aContext); | |||
| fIncludeCharRanges := TList.Create; | |||
| fExcludeCharRanges := TList.Create; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessor.Destroy; | |||
| begin | |||
| ClearRanges; | |||
| FreeAndNil(fIncludeCharRanges); | |||
| FreeAndNil(fExcludeCharRanges); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessorList////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorList.GetCount: Integer; | |||
| begin | |||
| result := fItems.Count; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorList.GetOwnsObjects: Boolean; | |||
| begin | |||
| result := fItems.OwnsObjects; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorList.SetOwnsObjects(const aValue: Boolean); | |||
| begin | |||
| fItems.OwnsObjects := aValue; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorList.Add(const aPostProcessor: TtsPostProcessor); | |||
| begin | |||
| fItems.Add(aPostProcessor); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorList.Delete(const aIndex: Integer); | |||
| begin | |||
| fItems.Delete(aIndex); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorList.Clear; | |||
| begin | |||
| fItems.Clear; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorList.Remove(const aPostProcessor: TtsPostProcessor): Integer; | |||
| begin | |||
| result := fItems.IndexOf(aPostProcessor); | |||
| if (result >= 0) then | |||
| fItems.Delete(result); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorList.IndexOf(const aPostProcessor: TtsPostProcessor): Integer; | |||
| begin | |||
| result := fItems.IndexOf(aPostProcessor); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorList.Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| result := inherited Execute(aChar, aImage); | |||
| if result then | |||
| for i := 0 to fItems.Count-1 do | |||
| (fItems[i] as TtsPostProcessor).Execute(aChar, aImage); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessorList.Create(const aContext: TtsContext; const aOwnsObjects: Boolean); | |||
| begin | |||
| inherited Create(aContext); | |||
| fItems := TObjectList.Create(aOwnsObjects); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessorList.Destroy; | |||
| begin | |||
| FreeAndNil(fItems); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessorFillColor///////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorFillColor.Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; | |||
| begin | |||
| result := inherited Execute(aChar, aImage); | |||
| if result and Assigned(aImage) then | |||
| aImage.FillColor(fColor, fChannels, fModes); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessorFillColor.Create(const aContext: TtsContext; const aColor: TtsColor4f; | |||
| const aModes: TtsImageModes; const aChannels: TtsColorChannels); | |||
| begin | |||
| inherited Create(aContext); | |||
| fColor := aColor; | |||
| fModes := aModes; | |||
| fChannels := aChannels; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessorFillPattern/////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorFillPattern.SetPattern(aValue: TtsImage); | |||
| begin | |||
| if (fPattern = aValue) then | |||
| exit; | |||
| if fOwnsPattern then | |||
| FreeAndNil(fPattern); | |||
| fPattern := aValue; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorFillPattern.Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; | |||
| begin | |||
| result := inherited Execute(aChar, aImage); | |||
| if result and Assigned(aImage) then | |||
| aImage.FillPattern(fPattern, fPosition.x, fPosition.y, fChannels, fModes); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessorFillPattern.Create(const aContext: TtsContext; const aPattern: TtsImage; | |||
| const aOwnsPattern: Boolean; const aPosition: TtsPosition; const aModes: TtsImageModes; const aChannels: TtsColorChannels); | |||
| begin | |||
| inherited Create(aContext); | |||
| fPattern := aPattern; | |||
| fOwnsPattern := aOwnsPattern; | |||
| fPosition := aPosition; | |||
| fModes := aModes; | |||
| fChannels := aChannels; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessorFillPattern.Destroy; | |||
| begin | |||
| if fOwnsPattern then | |||
| FreeAndNil(fPattern); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessorBorder//////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorBorder.SetStrength(const aValue: Single); | |||
| begin | |||
| fStrength := aValue; | |||
| FreeAndNil(fKernel); | |||
| fKernel := TtsKernel2D.Create(fWidth, fStrength); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorBorder.SetWidth(const aValue: Single); | |||
| begin | |||
| fWidth := aValue; | |||
| FreeAndNil(fKernel); | |||
| fKernel := TtsKernel2D.Create(fWidth, fStrength); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorBorder.Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; | |||
| var | |||
| orig: TtsImage; | |||
| x, y: Integer; | |||
| dst: PByte; | |||
| m: TtsGlyphMetric; | |||
| function BorderLookup: TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| c: TtsColor4f; | |||
| s: Single; | |||
| chan: TtsColorChannel; | |||
| mask: TtsColorChannels; | |||
| tmpX, tmpY: Integer; | |||
| begin | |||
| mask := TS_COLOR_CHANNELS_RGBA; | |||
| result := tsColor4f(0, 0, 0, 0); | |||
| for i := 0 to fKernel.ItemCount-1 do begin | |||
| tmpX := x + fKernel.Items[i].OffsetX; | |||
| tmpY := y + fKernel.Items[i].OffsetY; | |||
| if (tmpX >= 0) and (tmpX < orig.Width) and | |||
| (tmpY >= 0) and (tmpY < orig.Height) and | |||
| orig.GetPixelAt(tmpX, tmpY, c) then | |||
| begin | |||
| {$IFDEF FPC} | |||
| for chan in mask do begin | |||
| {$ELSE} | |||
| for chan := low(TtsColorChannel) to high(TtsColorChannel) do if (chan in mask) then begin | |||
| {$ENDIF} | |||
| s := c.arr[Integer(chan)] * fColor.arr[Integer(chan)] * fKernel.Items[i].Value; | |||
| if (s > result.arr[Integer(chan)]) then begin | |||
| result.arr[Integer(chan)] := s; | |||
| if (s >= 1.0) then begin | |||
| Exclude(mask, chan); | |||
| if (mask = []) then | |||
| exit; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| begin | |||
| result := inherited Execute(aChar, aImage); | |||
| if not result or not Assigned(aImage) then | |||
| exit; | |||
| aImage.Resize( | |||
| aImage.Width + 2 * fKernel.SizeX, | |||
| aImage.Height + 2 * fKernel.SizeY, | |||
| fKernel.SizeX, fKernel.SizeY); | |||
| orig := TtsImage.Create; | |||
| try | |||
| orig.Assign(aImage); | |||
| aImage.FillColor(fColor, TS_COLOR_CHANNELS_RGBA, TS_IMAGE_MODES_REPLACE_ALL); | |||
| for y := 0 to orig.Height-1 do begin | |||
| dst := aImage.Scanline[y]; | |||
| for x := 0 to orig.Width-1 do | |||
| tsFormatMap(aImage.Format, dst, BorderLookup); | |||
| end; | |||
| aImage.Blend(orig, 0, 0, @tsBlendColorAdditiveAlpha); | |||
| finally | |||
| FreeAndNil(orig); | |||
| end; | |||
| m := aChar.GlyphMetric; | |||
| m.GlyphRect.Right := m.GlyphRect.Right + 2 * fKernel.SizeX; | |||
| m.GlyphRect.Bottom := m.GlyphRect.Bottom + 2 * fKernel.SizeY; | |||
| if fKeepSize then begin | |||
| m.GlyphOrigin.x := m.GlyphOrigin.x - fKernel.SizeX; | |||
| m.GlyphOrigin.y := m.GlyphOrigin.y + fKernel.SizeY; | |||
| end else begin | |||
| m.Advance := m.Advance + 2 * fKernel.SizeX; | |||
| m.GlyphOrigin.y := m.GlyphOrigin.y + fKernel.SizeY; | |||
| end; | |||
| aChar.GlyphMetric := m; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessorBorder.Create(const aContext: TtsContext; | |||
| const aWidth, aStrength: Single; const aColor: TtsColor4f; const aKeepSize: Boolean); | |||
| begin | |||
| inherited Create(aContext); | |||
| fWidth := aWidth; | |||
| fStrength := aStrength; | |||
| fColor := aColor; | |||
| fKeepSize := aKeepSize; | |||
| fKernel := TtsKernel2D.Create(fWidth, fStrength); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessorBorder.Destroy; | |||
| begin | |||
| FreeAndNil(fKernel); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsPostProcessorShadow//////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorShadow.SetRadius(const aValue: Single); | |||
| begin | |||
| fRadius := aValue; | |||
| FreeAndNil(fKernel); | |||
| fKernel := TtsKernel1D.Create(fRadius, fStrength); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsPostProcessorShadow.SetStrength(const aValue: Single); | |||
| begin | |||
| fStrength := aValue; | |||
| FreeAndNil(fKernel); | |||
| fKernel := TtsKernel1D.Create(fRadius, fStrength); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsPostProcessorShadow.Execute(const aChar: TtsChar; const aImage: TtsImage): Boolean; | |||
| var | |||
| orig: TtsImage; | |||
| tmpX, tmpY: Integer; | |||
| m: TtsGlyphMetric; | |||
| begin | |||
| result := inherited Execute(aChar, aImage); | |||
| if not result or not Assigned(aImage) then | |||
| exit; | |||
| orig := TtsImage.Create; | |||
| try | |||
| orig.Assign(aImage); | |||
| aImage.Resize( | |||
| aImage.Width + 2 * fKernel.Size, | |||
| aImage.Height + 2 * fKernel.Size, | |||
| fKernel.Size, fKernel.Size); | |||
| aImage.FillColor(fColor, TS_COLOR_CHANNELS_RGBA, TS_IMAGE_MODES_MODULATE_ALPHA); | |||
| aImage.Blur(fKernel, fKernel, [tsChannelAlpha]); | |||
| tmpX := fKernel.Size - fOffset.x; | |||
| tmpY := fKernel.Size - fOffset.y; | |||
| aImage.Blend(orig, tmpX, tmpY, @tsBlendColorAlpha); | |||
| m := aChar.GlyphMetric; | |||
| m.GlyphRect.Right := m.GlyphRect.Right + 2 * fKernel.Size; | |||
| m.GlyphRect.Bottom := m.GlyphRect.Bottom + 2 * fKernel.Size; | |||
| m.GlyphOrigin.x := m.GlyphOrigin.x - tmpX; | |||
| m.GlyphOrigin.y := m.GlyphOrigin.y + tmpX; | |||
| aChar.GlyphMetric := m; | |||
| finally | |||
| FreeAndNil(orig); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsPostProcessorShadow.Create(const aContext: TtsContext; const aRadius, aStrength: Single; | |||
| const aOffset: TtsPosition; const aColor: TtsColor4f); | |||
| begin | |||
| inherited Create(aContext); | |||
| fRadius := aRadius; | |||
| fStrength := aStrength; | |||
| fOffset := aOffset; | |||
| fColor := aColor; | |||
| fKernel := TtsKernel1D.Create(fRadius, fStrength); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsPostProcessorShadow.Destroy; | |||
| begin | |||
| FreeAndNil(fKernel); | |||
| inherited Destroy; | |||
| end; | |||
| end. | |||
| @@ -0,0 +1,74 @@ | |||
| unit utsRenderer; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTypes, utsFont, utsCharCache, utsTextBlock; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsRenderer = class(TtsBlockRenderer) | |||
| public | |||
| function GetTextWidthA(const aFont: TtsFont; const aText: PAnsiChar): Integer; | |||
| function GetTextWidthW(const aFont: TtsFont; const aText: PWideChar): Integer; | |||
| function BeginBlock(const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags): TtsTextBlock; | |||
| public | |||
| class procedure EndBlock(var aBlock: TtsTextBlock); | |||
| class procedure AbortBlock(var aBlock: TtsTextBlock); | |||
| end; | |||
| implementation | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsRenderer/////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsRenderer.GetTextWidthA(const aFont: TtsFont; const aText: PAnsiChar): Integer; | |||
| var | |||
| c: TtsChars; | |||
| begin | |||
| result := 0; | |||
| c := CharCache.Chars[aFont]; | |||
| if Assigned(c) then | |||
| result := c.GetTextWidthA(aText); | |||
| end; | |||
| function TtsRenderer.GetTextWidthW(const aFont: TtsFont; const aText: PWideChar): Integer; | |||
| var | |||
| c: TtsChars; | |||
| begin | |||
| result := 0; | |||
| c := CharCache.Chars[aFont]; | |||
| if Assigned(c) then | |||
| result := c.GetTextWidthW(aText); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsRenderer.BeginBlock(const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags): TtsTextBlock; | |||
| begin | |||
| result := TtsTextBlock.Create(self, aTop, aLeft, aWidth, aHeight, aFlags); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| class procedure TtsRenderer.EndBlock(var aBlock: TtsTextBlock); | |||
| begin | |||
| try | |||
| aBlock.Render; | |||
| finally | |||
| FreeAndNil(aBlock); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| class procedure TtsRenderer.AbortBlock(var aBlock: TtsTextBlock); | |||
| begin | |||
| FreeAndNil(aBlock); | |||
| end; | |||
| end. | |||
| @@ -1,33 +1,33 @@ | |||
| unit utsRendererOpenGL; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTextSuite, utsTypes, utsOpenGLUtils, dglOpenGL; | |||
| Classes, SysUtils, dglOpenGL, | |||
| utsOpenGLUtils, utsTypes, utsContext, utsImage; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsRendererOpenGL = class(TtsBaseOpenGL) | |||
| private | |||
| fVBO: GLuint; | |||
| fIsRendering: Boolean; | |||
| protected | |||
| function CreateNewTexture: PtsFontTexture; 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 UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, Y: Integer); override; | |||
| procedure BeginRender; override; | |||
| procedure EndRender; override; | |||
| procedure SetDrawPos(const X, Y: Integer); override; | |||
| procedure MoveDrawPos(const X, Y: Integer); override; | |||
| procedure SetColor(const aColor: TtsColor4f); override; | |||
| procedure Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer); override; | |||
| procedure SetDrawPos(const aValue: TtsPosition); override; | |||
| procedure MoveDrawPos(const aOffset: TtsPosition); override; | |||
| procedure SetColor(const aValue: TtsColor4f); override; | |||
| procedure Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer = 0); override; | |||
| public | |||
| constructor Create(const aContext: TtsContext; const aFormat: TtsFormat); | |||
| destructor Destroy; override; | |||
| @@ -35,6 +35,9 @@ type | |||
| implementation | |||
| uses | |||
| utsUtils; | |||
| type | |||
| TVertex = packed record | |||
| pos: array[0..1] of GLfloat; | |||
| @@ -119,7 +122,8 @@ begin | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGL.UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer); | |||
| procedure TtsRendererOpenGL.UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, | |||
| Y: Integer); | |||
| begin | |||
| glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | |||
| @@ -156,40 +160,39 @@ begin | |||
| glPopMatrix; | |||
| fIsRendering := false; | |||
| end; | |||
| inherited EndRender; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGL.SetDrawPos(const X, Y: Integer); | |||
| procedure TtsRendererOpenGL.SetDrawPos(const aValue: TtsPosition); | |||
| begin | |||
| inherited SetDrawPos(X, Y); | |||
| inherited SetDrawPos(aValue); | |||
| glPopMatrix; | |||
| glPushMatrix; | |||
| glTranslatef(X, Y, 0); | |||
| glTranslatef(aValue.x, aValue.y, 0); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGL.MoveDrawPos(const X, Y: Integer); | |||
| procedure TtsRendererOpenGL.MoveDrawPos(const aOffset: TtsPosition); | |||
| begin | |||
| inherited MoveDrawPos(X, Y); | |||
| glTranslatef(X, Y, 0); | |||
| inherited MoveDrawPos(aOffset); | |||
| glTranslatef(aOffset.x, aOffset.y, 0); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGL.SetColor(const aColor: TtsColor4f); | |||
| procedure TtsRendererOpenGL.SetColor(const aValue: TtsColor4f); | |||
| begin | |||
| inherited SetColor(aColor); | |||
| inherited SetColor(aValue); | |||
| glColor4fv(@Color.arr[0]); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGL.Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer); | |||
| procedure TtsRendererOpenGL.Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer); | |||
| var | |||
| ref: TtsCharRenderRefOpenGL; | |||
| ref: TtsOpenGLRenderRef; | |||
| m: TtsMatrix4f; | |||
| begin | |||
| if Assigned(aCharRef) and (aCharRef is TtsCharRenderRefOpenGL) then begin | |||
| ref := (aCharRef as TtsCharRenderRefOpenGL); | |||
| if Assigned(aRenderRef) then begin | |||
| ref := TtsOpenGLRenderRef(aRenderRef); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, ref.TextureID); | |||
| @@ -228,6 +231,7 @@ begin | |||
| glBindBuffer(GL_ARRAY_BUFFER, 0); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsRendererOpenGL.Destroy; | |||
| begin | |||
| glDeleteBuffers(1, @fVBO); | |||
| @@ -1,15 +1,14 @@ | |||
| unit utsRendererOpenGLES; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| {.$DEFINE DEBUG} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsTextSuite, utsTypes, utsOpenGLUtils, dglOpenGLES; | |||
| Classes, SysUtils, dglOpenGLES, | |||
| utsOpenGLUtils, utsTypes, utsContext, utsImage; | |||
| type | |||
| TtsRendererOpenGLES = class(TtsBaseOpenGL) | |||
| @@ -36,15 +35,14 @@ type | |||
| protected | |||
| function CreateNewTexture: PtsFontTexture; override; | |||
| procedure FreeTexture(var aTexture: PtsFontTexture); override; | |||
| procedure UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; | |||
| const aCharImage: TtsImage; const X, Y: Integer); override; | |||
| procedure UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, Y: Integer); override; | |||
| procedure BeginRender; override; | |||
| procedure SetDrawPos(const X, Y: Integer); override; | |||
| procedure MoveDrawPos(const X, Y: Integer); override; | |||
| procedure SetColor(const aColor: TtsColor4f); override; | |||
| procedure Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer); override; | |||
| procedure SetDrawPos(const aValue: TtsPosition); override; | |||
| procedure MoveDrawPos(const aOffset: TtsPosition); override; | |||
| procedure SetColor(const aValue: TtsColor4f); override; | |||
| procedure Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer = 0); override; | |||
| public | |||
| property ShaderProgram: GLuint read fShaderProgram write SetShaderProgram; | |||
| property ProjectionMatrix: TtsMatrix4f read fProjMatrix write SetProjectionMatrix; | |||
| @@ -56,6 +54,9 @@ type | |||
| implementation | |||
| uses | |||
| utsUtils, utsConstants; | |||
| type | |||
| TVertex = packed record | |||
| pos: array[0..1] of GLfloat; | |||
| @@ -316,7 +317,8 @@ begin | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGLES.UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer); | |||
| procedure TtsRendererOpenGLES.UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, | |||
| Y: Integer); | |||
| begin | |||
| glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID); | |||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | |||
| @@ -335,34 +337,34 @@ begin | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGLES.SetDrawPos(const X, Y: Integer); | |||
| procedure TtsRendererOpenGLES.SetDrawPos(const aValue: TtsPosition); | |||
| begin | |||
| inherited SetDrawPos(X, Y); | |||
| inherited SetDrawPos(aValue); | |||
| UpdateUniformCharOffset; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGLES.MoveDrawPos(const X, Y: Integer); | |||
| procedure TtsRendererOpenGLES.MoveDrawPos(const aOffset: TtsPosition); | |||
| begin | |||
| inherited MoveDrawPos(X, Y); | |||
| inherited MoveDrawPos(aOffset); | |||
| UpdateUniformCharOffset; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGLES.SetColor(const aColor: TtsColor4f); | |||
| procedure TtsRendererOpenGLES.SetColor(const aValue: TtsColor4f); | |||
| begin | |||
| inherited SetColor(aColor); | |||
| inherited SetColor(aValue); | |||
| glColor4f(Color.r, Color.g, Color.b, Color.a); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRendererOpenGLES.Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer); | |||
| procedure TtsRendererOpenGLES.Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer); | |||
| var | |||
| ref: TtsCharRenderRefOpenGL; | |||
| ref: TtsOpenGLRenderRef; | |||
| m: TtsMatrix4f; | |||
| begin | |||
| if Assigned(aCharRef) and (aCharRef is TtsCharRenderRefOpenGL) then begin | |||
| ref := (aCharRef as TtsCharRenderRefOpenGL); | |||
| if Assigned(aRenderRef) then begin | |||
| ref := TtsOpenGLRenderRef(aRenderRef); | |||
| glBindTexture(GL_TEXTURE_2D, ref.TextureID); | |||
| glBindBuffer(GL_ARRAY_BUFFER, fVBO); | |||
| @@ -424,3 +426,4 @@ begin | |||
| end; | |||
| end. | |||
| @@ -0,0 +1,825 @@ | |||
| unit utsTextBlock; | |||
| {$IFDEF FPC} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, | |||
| utsUtils, utsTypes, utsFont, utsCharCache, utsContext; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsLineItemType = ( | |||
| tsItemTypeUnknown, | |||
| tsItemTypeFont, | |||
| tsItemTypeColor, | |||
| tsItemTypeText, | |||
| tsItemTypeSpace, | |||
| tsItemTypeLineBreak, | |||
| tsItemTypeTab, | |||
| tsItemTypeSpacing); | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsLineFlag = ( | |||
| tsLastItemIsSpace, // is set if the last item was a space item | |||
| tsMetaValid, // is set if the line meta data is valid | |||
| tsAutoLineBreak // is set if the linebreak was set automatically | |||
| ); | |||
| TtsLineFlags = set of TtsLineFlag; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| PtsLineItem = ^TtsLineItem; | |||
| TtsLineItem = packed record | |||
| Next: PtsLineItem; | |||
| Prev: PtsLineItem; | |||
| ItemType: TtsLineItemType; | |||
| case TtsLineItemType of | |||
| tsItemTypeFont: ( | |||
| Font: TtsFont | |||
| ); | |||
| tsItemTypeColor: ( | |||
| Color: TtsColor4f; | |||
| ); | |||
| tsItemTypeText, tsItemTypeSpace: ( | |||
| Text: PWideChar; // text of this item | |||
| TextWidth: Integer; // width of text (in pixel) | |||
| ); | |||
| tsItemTypeSpacing: ( | |||
| Spacing: Integer; | |||
| ); | |||
| tsItemTypeTab: ( | |||
| TabWidth: Integer; // with of tab (in pixel) | |||
| ); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| PtsBlockLine = ^TtsBlockLine; | |||
| TtsBlockLine = packed record | |||
| Next: PtsBlockLine; | |||
| First: PtsLineItem; | |||
| Last: PtsLineItem; | |||
| Flags: TtsLineFlags; | |||
| meta: packed record | |||
| Width: Integer; // absolut width of this line | |||
| Height: Integer; // absolute height of this line | |||
| Spacing: Integer; // spacing between lines | |||
| Ascent: Integer; // text ascent | |||
| SpaceCount: Integer; // number of words in this line | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsBlockRenderer = class(TtsRenderRefGenerator) | |||
| private | |||
| fCharCache: TtsCharCache; | |||
| protected | |||
| property CharCache: TtsCharCache read fCharCache; | |||
| procedure BeginRender; virtual; abstract; | |||
| procedure EndRender; virtual; abstract; | |||
| function GetDrawPos: TtsPosition; virtual; abstract; | |||
| procedure SetDrawPos(const aValue: TtsPosition); virtual; abstract; | |||
| procedure MoveDrawPos(const aOffset: TtsPosition); virtual; abstract; | |||
| procedure SetColor(const aValue: TtsColor4f); virtual; abstract; | |||
| procedure Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer = 0); virtual; abstract; | |||
| public | |||
| constructor Create(const aContext: TtsContext; const aFormat: TtsFormat); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsTextBlock = class(TtsRefManager) | |||
| private | |||
| fRenderer: TtsBlockRenderer; | |||
| fTop: Integer; | |||
| fLeft: Integer; | |||
| fWidth: Integer; | |||
| fHeight: Integer; | |||
| fFlags: TtsBlockFlags; | |||
| fVertAlign: TtsVertAlignment; | |||
| fHorzAlign: TtsHorzAlignment; | |||
| fClipping: TtsClipping; | |||
| fCurrentChars: TtsChars; | |||
| fCurrentColor: TtsColor4f; | |||
| fCurrentFont: TtsFont; | |||
| fFirstLine: PtsBlockLine; | |||
| fLastLine: PtsBlockLine; | |||
| function GetRect: TtsRect; | |||
| function PushLineItem(const aItem: PtsLineItem): Boolean; | |||
| procedure PushSpacing(const aWidth: Integer); | |||
| procedure PushNewLine; | |||
| procedure FreeLineItem(var aItem: PtsLineItem); | |||
| procedure FreeLineItems(var aItem: PtsLineItem); | |||
| procedure FreeLines(var aItem: PtsBlockLine); | |||
| function SplitText(aText: PWideChar): PtsLineItem; | |||
| function SplitIntoLines(aItem: PtsLineItem): Boolean; | |||
| procedure TrimSpaces(const aLine: PtsBlockLine); | |||
| procedure UpdateLineMeta(const aLine: PtsBlockLine); | |||
| public | |||
| procedure ChangeFont(const aFont: TtsFont); | |||
| procedure ChangeColor(const aColor: TtsColor4f); | |||
| public | |||
| property Rect: TtsRect read GetRect; | |||
| property Width: Integer read fWidth; | |||
| property Height: Integer read fHeight; | |||
| property Flags: TtsBlockFlags read fFlags; | |||
| property Top: Integer read fTop write fTop; | |||
| property Left: Integer read fLeft write fLeft; | |||
| property VertAlign: TtsVertAlignment read fVertAlign write fVertAlign; | |||
| property HorzAlign: TtsHorzAlignment read fHorzAlign write fHorzAlign; | |||
| property Clipping: TtsClipping read fClipping write fClipping; | |||
| property CurrentColor: TtsColor4f read fCurrentColor write ChangeColor; | |||
| property CurrentFont: TtsFont read fCurrentFont write ChangeFont; | |||
| function GetActualBlockHeight: Integer; | |||
| procedure TextOutA(const aText: PAnsiChar); | |||
| procedure TextOutW(const aText: PWideChar); | |||
| function GetTextWidthA(const aText: PAnsiChar): Integer; | |||
| function GetTextWidthW(const aText: PWideChar): Integer; | |||
| procedure Render; | |||
| constructor Create(const aRenderer: TtsBlockRenderer; const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags); | |||
| destructor Destroy; override; | |||
| end; | |||
| implementation | |||
| uses | |||
| math, | |||
| utsChar; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsBlockRenderer////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsBlockRenderer.Create(const aContext: TtsContext; const aFormat: TtsFormat); | |||
| begin | |||
| inherited Create(aContext, aFormat); | |||
| fCharCache := TtsCharCache.Create(self); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsBlockRenderer.Destroy; | |||
| begin | |||
| FreeAndNil(fCharCache); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsTextBlock////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.GetRect: TtsRect; | |||
| begin | |||
| result.Left := fLeft; | |||
| result.Top := fTop; | |||
| result.Right := fLeft + fWidth; | |||
| result.Bottom := fTop + fHeight; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.PushLineItem(const aItem: PtsLineItem): Boolean; | |||
| begin | |||
| result := false; | |||
| if not Assigned(fLastLine) then | |||
| PushNewLine; | |||
| if not Assigned(fLastLine^.First) and | |||
| (aItem^.ItemType in [tsItemTypeSpace, tsItemTypeSpacing]) then | |||
| exit; // di not add line space or line spacing if line is empty | |||
| if Assigned(fLastLine^.Last) then begin | |||
| aItem^.Prev := fLastLine^.Last; | |||
| aItem^.Next := nil; | |||
| fLastLine^.Last^.Next := aItem; | |||
| fLastLine^.Last := aItem; | |||
| end; | |||
| if not Assigned(fLastLine^.First) then begin | |||
| fLastLine^.First := aItem; | |||
| fLastLine^.Last := aItem; | |||
| end; | |||
| case aItem^.ItemType of | |||
| tsItemTypeSpace, tsItemTypeText: | |||
| fLastLine^.meta.Width := fLastLine^.meta.Width + aItem^.TextWidth; | |||
| tsItemTypeSpacing: | |||
| fLastLine^.meta.Width := fLastLine^.meta.Width + aItem^.Spacing; | |||
| tsItemTypeTab: | |||
| fLastLine^.meta.Width := fLastLine^.meta.Width + aItem^.TabWidth; | |||
| end; | |||
| result := true; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.PushSpacing(const aWidth: Integer); | |||
| var | |||
| p: PtsLineItem; | |||
| begin | |||
| if (aWidth <= 0) then | |||
| exit; | |||
| new(p); | |||
| FillChar(p^, SizeOf(p^), #0); | |||
| p^.ItemType := tsItemTypeSpacing; | |||
| p^.Spacing := aWidth; | |||
| PushLineItem(p); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.PushNewLine; | |||
| var | |||
| p: PtsBlockLine; | |||
| begin | |||
| TrimSpaces(fLastLine); | |||
| new(p); | |||
| FillChar(p^, SizeOf(p^), #0); | |||
| UpdateLineMeta(p); | |||
| if Assigned(fLastLine) then begin | |||
| fLastLine^.Next := p; | |||
| fLastLine := p; | |||
| end; | |||
| if not Assigned(fFirstLine) then begin | |||
| fFirstLine := p; | |||
| fLastLine := p; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.FreeLineItem(var aItem: PtsLineItem); | |||
| begin | |||
| if Assigned(aItem^.Prev) then | |||
| aItem^.Prev^.Next := aItem^.Next; | |||
| if Assigned(aItem^.Next) then | |||
| aItem^.Next^.Prev := aItem^.Prev; | |||
| case aItem^.ItemType of | |||
| tsItemTypeText, tsItemTypeSpace: | |||
| tsStrDispose(aItem^.Text); | |||
| end; | |||
| Dispose(aItem); | |||
| aItem := nil; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.FreeLineItems(var aItem: PtsLineItem); | |||
| var | |||
| p: PtsLineItem; | |||
| begin | |||
| while Assigned(aItem) do begin | |||
| p := aItem; | |||
| aItem := aItem^.Next; | |||
| FreeLineItem(p); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.FreeLines(var aItem: PtsBlockLine); | |||
| var | |||
| p: PtsBlockLine; | |||
| begin | |||
| while Assigned(aItem) do begin | |||
| p := aItem; | |||
| aItem := aItem^.Next; | |||
| FreeLineItems(p^.First); | |||
| p^.Last := nil; | |||
| Dispose(p); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.SplitText(aText: PWideChar): PtsLineItem; | |||
| var | |||
| TextBegin: PWideChar; | |||
| TextLength: Integer; | |||
| State: TtsLineItemType; | |||
| LastItem: PtsLineItem; | |||
| procedure AddItem(const aItem: PtsLineItem); | |||
| begin | |||
| if Assigned(result) then begin | |||
| LastItem^.Next := aItem; | |||
| aItem^.Prev := LastItem; | |||
| aItem^.Next := nil; | |||
| LastItem := aItem; | |||
| end; | |||
| if not Assigned(result) then begin | |||
| result := aItem; | |||
| LastItem := aItem; | |||
| end; | |||
| end; | |||
| procedure ExtractWord; | |||
| var | |||
| p: PtsLineItem; | |||
| Text: PWideChar; | |||
| begin | |||
| if (State = tsItemTypeUnknown) then | |||
| exit; | |||
| new(p); | |||
| FillChar(p^, SizeOf(p^), #0); | |||
| p^.ItemType := State; | |||
| case State of | |||
| tsItemTypeText, tsItemTypeSpace: begin | |||
| p^.Text := tsStrAlloc(TextLength); | |||
| Text := p^.Text; | |||
| while (TextBegin <> aText) do begin | |||
| Text^ := TextBegin^; | |||
| inc(Text, 1); | |||
| inc(TextBegin, 1); | |||
| end; | |||
| AddItem(p); | |||
| end; | |||
| tsItemTypeLineBreak, tsItemTypeTab: begin | |||
| AddItem(p); | |||
| end; | |||
| else | |||
| Dispose(p); | |||
| end; | |||
| TextBegin := aText; | |||
| TextLength := 0; | |||
| end; | |||
| begin | |||
| result := nil; | |||
| LastItem := nil; | |||
| TextBegin := aText; | |||
| TextLength := 0; | |||
| State := tsItemTypeUnknown; | |||
| if not Assigned(aText) then | |||
| exit; | |||
| while (aText^ <> #0) do begin | |||
| case aText^ of | |||
| // line breaks | |||
| #$000D, #$000A: begin | |||
| if (State <> tsItemTypeLineBreak) then begin | |||
| ExtractWord; | |||
| State := tsItemTypeLineBreak; | |||
| end else if (TextBegin^ <> #13) or (aText^ <> #10) or (TextBegin + 1 < aText) then | |||
| ExtractWord; | |||
| end; | |||
| // spaces | |||
| #$0020: begin | |||
| if (State <> tsItemTypeSpace) then | |||
| ExtractWord; | |||
| State := tsItemTypeSpace; | |||
| end; | |||
| // tabulator | |||
| #$0009: begin | |||
| ExtractWord; | |||
| State := tsItemTypeTab; | |||
| end; | |||
| else | |||
| if (State <> tsItemTypeText) then | |||
| ExtractWord; | |||
| State := tsItemTypeText; | |||
| end; | |||
| inc(aText, 1); | |||
| inc(TextLength, 1); | |||
| end; | |||
| if (TextBegin <> aText) then | |||
| ExtractWord; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.SplitIntoLines(aItem: PtsLineItem): Boolean; | |||
| var | |||
| p: PtsLineItem; | |||
| tab: Integer; | |||
| begin | |||
| result := false; | |||
| if not Assigned(fCurrentFont) then | |||
| exit; | |||
| result := true; | |||
| while Assigned(aItem) do begin | |||
| p := aItem; | |||
| aItem := aItem^.Next; | |||
| p^.Next := nil; | |||
| p^.Prev := nil; | |||
| if not Assigned(fLastLine) then | |||
| PushNewLine; | |||
| case p^.ItemType of | |||
| tsItemTypeText, tsItemTypeSpace: begin | |||
| // increment word counter | |||
| if (p^.ItemType = tsItemTypeSpace) then begin | |||
| if not (tsLastItemIsSpace in fLastLine^.Flags) then | |||
| inc(fLastLine^.meta.SpaceCount, 1); | |||
| Include(fLastLine^.Flags, tsLastItemIsSpace); | |||
| end else | |||
| Exclude(fLastLine^.Flags, tsLastItemIsSpace); | |||
| // update and check line width | |||
| p^.TextWidth := GetTextWidthW(p^.Text); | |||
| if (tsBlockFlagWordWrap in fFlags) and | |||
| (fLastLine^.meta.Width + p^.TextWidth > fWidth) then | |||
| begin | |||
| if (fLastLine^.meta.Width = 0) then begin | |||
| if not PushLineItem(p) then // if is first word, than add anyway | |||
| FreeLineItem(p); | |||
| p := nil; | |||
| end; | |||
| include(fLastLine^.Flags, tsAutoLineBreak); | |||
| PushNewLine; | |||
| end; | |||
| // add item | |||
| if Assigned(p) then begin | |||
| if not PushLineItem(p) then | |||
| FreeLineItem(p); | |||
| PushSpacing(fCurrentFont.CharSpacing); | |||
| end; | |||
| end; | |||
| tsItemTypeLineBreak: begin | |||
| if not PushLineItem(p) then | |||
| FreeLineItem(p); | |||
| PushNewLine; | |||
| end; | |||
| tsItemTypeTab: begin | |||
| tab := fCurrentFont.TabWidth * fCurrentFont.Metric.Size; | |||
| p^.TabWidth := (1 + fLastLine^.meta.Width div tab) * tab - fLastLine^.meta.Width; | |||
| if not PushLineItem(p) then | |||
| FreeLineItem(p); | |||
| end; | |||
| else | |||
| raise EtsException.Create('unexpected line item'); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.TrimSpaces(const aLine: PtsBlockLine); | |||
| procedure Trim(var aItem: PtsLineItem; const aMoveNext: Boolean); | |||
| var | |||
| tmp, p: PtsLineItem; | |||
| IsFirst: Boolean; | |||
| begin | |||
| IsFirst := true; | |||
| p := aItem; | |||
| while Assigned(p) do begin | |||
| tmp := p; | |||
| if aMoveNext then | |||
| p := p^.Next | |||
| else | |||
| p := p^.Prev; | |||
| case tmp^.ItemType of | |||
| tsItemTypeText: begin //done | |||
| break; | |||
| end; | |||
| tsItemTypeSpace, | |||
| tsItemTypeSpacing: begin | |||
| // update line meta | |||
| if (tmp^.ItemType = tsItemTypeSpace) then begin | |||
| aLine^.meta.Width := aLine^.meta.Width - tmp^.TextWidth; | |||
| dec(aLine^.meta.SpaceCount, 1); | |||
| end else | |||
| aLine^.meta.Width := aLine^.meta.Width - tmp^.Spacing; | |||
| FreeLineItem(tmp); | |||
| if IsFirst then | |||
| aItem := p; | |||
| end; | |||
| else | |||
| IsFirst := false; | |||
| end; | |||
| end; | |||
| end; | |||
| begin | |||
| if not Assigned(aLine) then | |||
| exit; | |||
| Trim(aLine^.First, true); | |||
| Trim(aLine^.Last, false); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.UpdateLineMeta(const aLine: PtsBlockLine); | |||
| var | |||
| metric: TtsTextMetric; | |||
| begin | |||
| if not Assigned(fCurrentFont) or | |||
| not Assigned(aLine) then | |||
| exit; | |||
| fCurrentFont.GetTextMetric(metric); | |||
| if (tsMetaValid in aLine^.Flags) then begin | |||
| aLine^.meta.Height := max( | |||
| aLine^.meta.Height, | |||
| metric.LineHeight); | |||
| aLine^.meta.Spacing := max( | |||
| aLine^.meta.Spacing, | |||
| metric.LineSpacing); | |||
| aLine^.meta.Ascent := max( | |||
| aLine^.meta.Ascent, | |||
| metric.Ascent); | |||
| end else begin | |||
| Include(aLine^.Flags, tsMetaValid); | |||
| aLine^.meta.Height := metric.LineHeight; | |||
| aLine^.meta.Spacing := metric.LineSpacing; | |||
| aLine^.meta.Ascent := metric.Ascent; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.ChangeFont(const aFont: TtsFont); | |||
| var | |||
| p: PtsLineItem; | |||
| begin | |||
| if not Assigned(aFont) then | |||
| exit; | |||
| New(p); | |||
| FillChar(p^, SizeOf(p^), #0); | |||
| fCurrentFont := aFont; | |||
| fCurrentChars := fRenderer.fCharCache.Chars[fCurrentFont]; | |||
| p^.ItemType := tsItemTypeFont; | |||
| p^.Font := fCurrentFont; | |||
| PushLineItem(p); | |||
| UpdateLineMeta(fLastLine); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.ChangeColor(const aColor: TtsColor4f); | |||
| var | |||
| p: PtsLineItem; | |||
| begin | |||
| New(p); | |||
| FillChar(p^, SizeOf(p^), #0); | |||
| fCurrentColor := aColor; | |||
| p^.ItemType := tsItemTypeColor; | |||
| p^.Color := fCurrentColor; | |||
| PushLineItem(p); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.GetActualBlockHeight: Integer; | |||
| var | |||
| line: PtsBlockLine; | |||
| begin | |||
| result := 0; | |||
| line := fFirstLine; | |||
| while Assigned(line) do begin | |||
| result := result + line^.meta.Height; | |||
| line := line^.Next; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.TextOutA(const aText: PAnsiChar); | |||
| var | |||
| tmp: PWideChar; | |||
| begin | |||
| tmp := fRenderer.Context.AnsiToWide(aText); | |||
| try | |||
| TextOutW(tmp); | |||
| finally | |||
| tsStrDispose(tmp); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.TextOutW(const aText: PWideChar); | |||
| var | |||
| p: PtsLineItem; | |||
| begin | |||
| p := SplitText(aText); | |||
| if not SplitIntoLines(p) then | |||
| FreeLineItems(p); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.GetTextWidthA(const aText: PAnsiChar): Integer; | |||
| begin | |||
| result := 0; | |||
| if Assigned(fCurrentChars) then | |||
| result := fCurrentChars.GetTextWidthA(aText); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function TtsTextBlock.GetTextWidthW(const aText: PWideChar): Integer; | |||
| begin | |||
| result := 0; | |||
| if Assigned(fCurrentChars) then | |||
| result := fCurrentChars.GetTextWidthW(aText); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsTextBlock.Render; | |||
| var | |||
| c: PWideChar; | |||
| pos: TtsPosition; | |||
| x, y, tmp, tab: Integer; | |||
| ExtraSpaceTotal, ExtraSpaceActual: Single; | |||
| r: TtsRect; | |||
| line: PtsBlockLine; | |||
| item: PtsLineItem; | |||
| font: TtsFont; | |||
| chars: TtsChars; | |||
| char: TtsChar; | |||
| metric: TtsTextMetric; | |||
| draw: Boolean; | |||
| procedure DrawItem; | |||
| begin | |||
| case item^.ItemType of | |||
| tsItemTypeFont: begin | |||
| font := item^.Font; | |||
| font.GetTextMetric(metric); | |||
| chars := fRenderer.fCharCache.Chars[font]; | |||
| end; | |||
| tsItemTypeColor: begin | |||
| fRenderer.SetColor(item^.Color); | |||
| end; | |||
| tsItemTypeText: begin | |||
| if draw and Assigned(font) then begin | |||
| c := item^.Text; | |||
| while (c^ <> #0) do begin | |||
| if Assigned(chars) then begin | |||
| char := chars.AddChar(c^); | |||
| if Assigned(char) then begin | |||
| fRenderer.MoveDrawPos(tsPosition(0, -metric.BaseLineOffset)); | |||
| fRenderer.Render(char.RenderRef); | |||
| fRenderer.MoveDrawPos(tsPosition(char.GlyphMetric.Advance + font.CharSpacing, metric.BaseLineOffset)); | |||
| end; | |||
| end; | |||
| inc(c); | |||
| end; | |||
| end; | |||
| end; | |||
| tsItemTypeSpace: begin | |||
| if draw and Assigned(font) then begin | |||
| ExtraSpaceActual := ExtraSpaceActual + ExtraSpaceTotal; | |||
| c := item^.Text; | |||
| while (c^ <> #0) do begin | |||
| if Assigned(chars) then begin | |||
| char := chars.AddChar(c^); | |||
| if Assigned(char) then begin | |||
| if (font.Metric.Style * [tsStyleUnderline, tsStyleStrikeout] <> []) then begin | |||
| fRenderer.MoveDrawPos(tsPosition(0, -metric.BaseLineOffset)); | |||
| fRenderer.Render(char.RenderRef); | |||
| fRenderer.MoveDrawPos(tsPosition(char.GlyphMetric.Advance + font.CharSpacing, metric.BaseLineOffset)); | |||
| end else begin | |||
| fRenderer.MoveDrawPos(tsPosition(char.GlyphMetric.Advance + font.CharSpacing, 0)); | |||
| end; | |||
| end; | |||
| end; | |||
| inc(c); | |||
| end; | |||
| tmp := Trunc(ExtraSpaceActual); | |||
| ExtraSpaceActual := ExtraSpaceActual - tmp; | |||
| if (font.Metric.Style * [tsStyleUnderline, tsStyleStrikeout] <> []) then begin | |||
| if Assigned(chars) then begin | |||
| char := chars.AddChar(#0); | |||
| if Assigned(char) then | |||
| fRenderer.Render(char.RenderRef, tmp); | |||
| // TODO draw lines; maybe with a temporary created fake char or something like an empty char? | |||
| end; | |||
| end; | |||
| fRenderer.MoveDrawPos(tsPosition(tmp, 0)); | |||
| end; | |||
| end; | |||
| tsItemTypeLineBreak: begin | |||
| // because this should be the last item in a line, we have nothing to do here | |||
| end; | |||
| tsItemTypeTab: begin | |||
| // get current x pos and round it to TabWidth | |||
| pos := fRenderer.GetDrawPos; | |||
| tab := font.TabWidth * font.Metric.Size; | |||
| if (tab = 0) then | |||
| tab := 1; | |||
| pos.x := Left + (1 + (pos.x - Left) div tab) * tab; | |||
| fRenderer.SetDrawPos(pos); | |||
| end; | |||
| tsItemTypeSpacing: begin | |||
| fRenderer.MoveDrawPos(tsPosition(item^.Spacing, 0)); | |||
| end; | |||
| end; | |||
| end; | |||
| procedure DrawLine; | |||
| begin | |||
| // check vertical clipping | |||
| case Clipping of | |||
| tsClipCharBorder, tsClipWordBorder: | |||
| draw := (y + line^.meta.Height > r.Top) and (y < r.Bottom); | |||
| tsClipCharComplete, tsClipWordComplete: | |||
| draw := (y > r.Top) and (y + line^.meta.Height < r.Bottom); | |||
| else | |||
| draw := true; | |||
| end; | |||
| // check horizontal alignment | |||
| x := r.Left; | |||
| ExtraSpaceTotal := 0; | |||
| ExtraSpaceActual := 0; | |||
| case HorzAlign of | |||
| tsHorzAlignCenter: | |||
| x := r.Left + (Width div 2) - (line^.meta.Width div 2); | |||
| tsHorzAlignRight: | |||
| x := r.Right - line^.meta.Width; | |||
| tsHorzAlignJustify: | |||
| if (tsAutoLineBreak in line^.Flags) and (line^.meta.SpaceCount > 0) then | |||
| ExtraSpaceTotal := (Width - line^.meta.Width) / line^.meta.SpaceCount; | |||
| end; | |||
| if draw then | |||
| fRenderer.SetDrawPos(tsPosition(x, y + line^.meta.Ascent)); | |||
| inc(y, line^.meta.Height + line^.meta.Spacing); | |||
| item := line^.First; | |||
| while Assigned(item) do begin | |||
| DrawItem; | |||
| item := item^.Next; | |||
| end; | |||
| end; | |||
| begin | |||
| fRenderer.BeginRender; | |||
| try | |||
| // init variables | |||
| y := Top; | |||
| r := Rect; | |||
| font := nil; | |||
| line := fFirstLine; | |||
| // check vertical alignment | |||
| case VertAlign of | |||
| tsVertAlignCenter: | |||
| y := y + (Height div 2 - GetActualBlockHeight div 2); | |||
| tsVertAlignBottom: | |||
| y := y + (Height - GetActualBlockHeight); | |||
| end; | |||
| while Assigned(line) do begin | |||
| DrawLine; | |||
| line := line^.Next; | |||
| end; | |||
| finally | |||
| fRenderer.EndRender; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsTextBlock.Create(const aRenderer: TtsBlockRenderer; const aTop, aLeft, aWidth, aHeight: Integer; const aFlags: TtsBlockFlags); | |||
| begin | |||
| inherited Create(aRenderer); | |||
| fRenderer := aRenderer; | |||
| fTop := aTop; | |||
| fLeft := aLeft; | |||
| fWidth := aWidth; | |||
| fHeight := aHeight; | |||
| fFlags := aFlags; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsTextBlock.Destroy; | |||
| begin | |||
| FreeLines(fFirstLine); | |||
| fLastLine := nil; | |||
| inherited Destroy; | |||
| end; | |||
| end. | |||
| @@ -1,326 +0,0 @@ | |||
| unit utsTtfUtils; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils; | |||
| const | |||
| NAME_ID_COPYRIGHT = 0; | |||
| NAME_ID_FACE_NAME = 1; | |||
| NAME_ID_STYLE_NAME = 2; | |||
| NAME_ID_FULL_NAME = 4; | |||
| function MakeTTTableName(const ch1, ch2, ch3, ch4: Char): Cardinal; | |||
| function GetTTString(pBuffer: Pointer; BufferSize: Integer; NameID, LanguageID: Cardinal; out Text: String): Boolean; | |||
| function GetTTFontFullNameFromStream(Stream: TStream; LanguageID: Cardinal): String; | |||
| function GetTTFontFullNameFromFile(const aFilename: String; const aLanguageID: Cardinal): String; | |||
| implementation | |||
| uses | |||
| utsUtils; | |||
| type | |||
| TT_OFFSET_TABLE = packed record | |||
| uMajorVersion: Word; | |||
| uMinorVersion: Word; | |||
| uNumOfTables: Word; | |||
| uSearchRange: Word; | |||
| uEntrySelector: Word; | |||
| uRangeShift: Word; | |||
| end; | |||
| TT_TABLE_DIRECTORY = packed record | |||
| TableName: Cardinal; // table name | |||
| uCheckSum: Cardinal; // Check sum | |||
| uOffset: Cardinal; // Offset from beginning of file | |||
| uLength: Cardinal; // length of the table in bytes | |||
| end; | |||
| TT_NAME_TABLE_HEADER = packed record | |||
| uFSelector: Word; //format selector. Always 0 | |||
| uNRCount: Word; //Name Records count | |||
| uStorageOffset: Word; //Offset for strings storage, from start of the table | |||
| end; | |||
| TT_NAME_RECORD = packed record | |||
| uPlatformID: Word; | |||
| uEncodingID: Word; | |||
| uLanguageID: Word; | |||
| uNameID: Word; | |||
| uStringLength: Word; | |||
| uStringOffset: Word; //from start of storage area | |||
| end; | |||
| const | |||
| PLATFORM_ID_APPLE_UNICODE = 0; | |||
| PLATFORM_ID_MACINTOSH = 1; | |||
| PLATFORM_ID_MICROSOFT = 3; | |||
| function SWAPWORD(x: Word): Word; | |||
| begin | |||
| Result := x and $FF; | |||
| Result := Result shl 8; | |||
| Result := Result or (x shr 8); | |||
| end; | |||
| function SWAPLONG(x: Cardinal): Cardinal; | |||
| begin | |||
| Result := (x and $FF) shl 24; | |||
| x := x shr 8; | |||
| Result := Result or ((x and $FF) shl 16); | |||
| x := x shr 8; | |||
| Result := Result or ((x and $FF) shl 8); | |||
| x := x shr 8; | |||
| Result := Result or x; | |||
| end; | |||
| function GetTTTableData(Stream: TStream; TableName: Cardinal; pBuff: Pointer; var Size: Integer): Boolean; | |||
| var | |||
| Pos: Int64; | |||
| OffsetTable: TT_OFFSET_TABLE; | |||
| TableDir: TT_TABLE_DIRECTORY; | |||
| Idx: Integer; | |||
| begin | |||
| Result := False; | |||
| Pos := Stream.Position; | |||
| // Reading table header | |||
| Stream.Read(OffsetTable{%H-}, sizeof(TT_OFFSET_TABLE)); | |||
| OffsetTable.uNumOfTables := SWAPWORD(OffsetTable.uNumOfTables); | |||
| OffsetTable.uMajorVersion := SWAPWORD(OffsetTable.uMajorVersion); | |||
| OffsetTable.uMinorVersion := SWAPWORD(OffsetTable.uMinorVersion); | |||
| //check is this is a true type font and the version is 1.0 | |||
| if (OffsetTable.uMajorVersion <> 1) or (OffsetTable.uMinorVersion <> 0) then | |||
| Exit; | |||
| // seaching table with name | |||
| for Idx := 0 to OffsetTable.uNumOfTables -1 do begin | |||
| Stream.Read(TableDir{%H-}, sizeof(TT_TABLE_DIRECTORY)); | |||
| if (TableName = TableDir.TableName) then begin | |||
| TableDir.uOffset := SWAPLONG(TableDir.uOffset); | |||
| TableDir.uLength := SWAPLONG(TableDir.uLength); | |||
| // copying tabledata | |||
| if (pBuff <> nil) and (Size >= Integer(TableDir.uLength)) then begin | |||
| Stream.Seek(TableDir.uOffset, soBeginning); | |||
| Size := Stream.Read(pBuff^, TableDir.uLength); | |||
| Result := (Size = Integer(TableDir.uLength)); | |||
| end else | |||
| begin | |||
| // restoring streamposition | |||
| Stream.Position := Pos; | |||
| Size := TableDir.uLength; | |||
| Result := True; | |||
| end; | |||
| break; | |||
| end; | |||
| end; | |||
| end; | |||
| function MakeTTTableName(const ch1, ch2, ch3, ch4: Char): Cardinal; | |||
| begin | |||
| Result := ord(ch4) shl 24 or ord(ch3) shl 16 or ord(ch2) shl 8 or ord(ch1); | |||
| end; | |||
| function GetTTString(pBuffer: Pointer; BufferSize: Integer; NameID, LanguageID: Cardinal; out Text: String): Boolean; | |||
| var | |||
| pActBuffer: pByte; | |||
| ttNTHeader: TT_NAME_TABLE_HEADER; | |||
| ttRecord: TT_NAME_RECORD; | |||
| Idx: Integer; | |||
| Prio: Integer; | |||
| procedure ExtractName; | |||
| var | |||
| pTempBuffer: pByte; | |||
| pTemp: pWideChar; | |||
| uStringLengthH2: Word; | |||
| procedure SwapText(pText: pWideChar; Length: Word); | |||
| begin | |||
| while Length > 0 do begin | |||
| pWord(pText)^ := SWAPWORD(pWord(pText)^); | |||
| Inc(pText); | |||
| Dec(Length); | |||
| end; | |||
| end; | |||
| begin | |||
| Result := True; | |||
| ttRecord.uStringLength := SWAPWORD(ttRecord.uStringLength); | |||
| ttRecord.uStringOffset := SWAPWORD(ttRecord.uStringOffset); | |||
| uStringLengthH2 := ttRecord.uStringLength shr 1; | |||
| pTempBuffer := pBuffer; | |||
| Inc(pTempBuffer, ttNTHeader.uStorageOffset + ttRecord.uStringOffset); | |||
| // Unicode | |||
| if ((ttRecord.uPlatformID = PLATFORM_ID_MICROSOFT) and (ttRecord.uEncodingID in [0, 1])) or | |||
| ((ttRecord.uPlatformID = PLATFORM_ID_APPLE_UNICODE) and (ttRecord.uEncodingID > 0)) then begin | |||
| pTemp := tsStrAlloc(uStringLengthH2); | |||
| try | |||
| // uStringLengthH2 * 2 because possible buffer overrun | |||
| Move(pTempBuffer^, pTemp^, uStringLengthH2 * 2); | |||
| SwapText(pTemp, uStringLengthH2); | |||
| WideCharLenToStrVar(pTemp, uStringLengthH2, Text); | |||
| finally | |||
| tsStrDispose(pTemp); | |||
| end; | |||
| end else | |||
| // none unicode | |||
| begin | |||
| SetLength(Text, ttRecord.uStringLength); | |||
| Move(pTempBuffer^, Text[1], ttRecord.uStringLength); | |||
| end; | |||
| end; | |||
| begin | |||
| Result := False; | |||
| pActBuffer := pBuffer; | |||
| Move(pActBuffer^, ttNTHeader{%H-}, sizeof(TT_NAME_TABLE_HEADER)); | |||
| inc(pActBuffer, sizeof(TT_NAME_TABLE_HEADER)); | |||
| ttNTHeader.uNRCount := SWAPWORD(ttNTHeader.uNRCount); | |||
| ttNTHeader.uStorageOffset := SWAPWORD(ttNTHeader.uStorageOffset); | |||
| Prio := -1; | |||
| for Idx := 0 to ttNTHeader.uNRCount -1 do begin | |||
| Move(pActBuffer^, ttRecord, sizeof(TT_NAME_RECORD)); | |||
| Inc(pActBuffer, sizeof(TT_NAME_RECORD)); | |||
| ttRecord.uNameID := SWAPWORD(ttRecord.uNameID); | |||
| if ttRecord.uNameID = NameID then begin | |||
| ttRecord.uPlatformID := SWAPWORD(ttRecord.uPlatformID); | |||
| ttRecord.uEncodingID := SWAPWORD(ttRecord.uEncodingID); | |||
| ttRecord.uLanguageID := SWAPWORD(ttRecord.uLanguageID); | |||
| // highest priority | |||
| if (ttRecord.uPlatformID = PLATFORM_ID_MICROSOFT) then begin | |||
| // system language | |||
| if (ttRecord.uLanguageID = languageID) then begin | |||
| if Prio <= 7 then begin | |||
| ExtractName; | |||
| Prio := 7; | |||
| end; | |||
| end else | |||
| // english | |||
| if (ttRecord.uLanguageID = 1033) then begin | |||
| if Prio <= 6 then begin | |||
| ExtractName; | |||
| Prio := 6; | |||
| end; | |||
| end else | |||
| // all else | |||
| if Prio <= 5 then begin | |||
| ExtractName; | |||
| Prio := 5; | |||
| end; | |||
| end else | |||
| // apple unicode | |||
| if (ttRecord.uPlatformID = PLATFORM_ID_APPLE_UNICODE) then begin | |||
| ExtractName; | |||
| Prio := 4; | |||
| end else | |||
| // macintosh | |||
| if (ttRecord.uPlatformID = PLATFORM_ID_MACINTOSH) then begin | |||
| // english | |||
| if (ttRecord.uLanguageID = 0) then begin | |||
| if Prio <= 3 then begin | |||
| ExtractName; | |||
| Prio := 3; | |||
| end; | |||
| end else | |||
| // all other | |||
| begin | |||
| ExtractName; | |||
| Prio := 2; | |||
| end; | |||
| end else | |||
| begin | |||
| if Prio <= 1 then begin | |||
| ExtractName; | |||
| Prio := 1; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| end; | |||
| function GetTTFontFullNameFromStream(Stream: TStream; LanguageID: Cardinal): String; | |||
| var | |||
| TableName: Cardinal; | |||
| Buffer: Pointer; | |||
| BufferSize: Integer; | |||
| begin | |||
| TableName := MakeTTTableName('n', 'a', 'm', 'e'); | |||
| BufferSize := 0; | |||
| if GetTTTableData(Stream, TableName, nil, BufferSize) then begin | |||
| GetMem(Buffer, BufferSize); | |||
| try | |||
| if GetTTTableData(Stream, TableName, Buffer, BufferSize) then begin | |||
| if not GetTTString(Buffer, BufferSize, NAME_ID_FULL_NAME, LanguageID, Result) then | |||
| if not GetTTString(Buffer, BufferSize, NAME_ID_FACE_NAME, LanguageID, Result) then | |||
| Result := ''; | |||
| end; | |||
| finally | |||
| FreeMem(Buffer); | |||
| end; | |||
| end; | |||
| end; | |||
| function GetTTFontFullNameFromFile(const aFilename: String; const aLanguageID: Cardinal): String; | |||
| var | |||
| fs: TFileStream; | |||
| begin | |||
| fs := TFileStream.Create(aFilename, fmOpenRead or fmShareDenyWrite); | |||
| try | |||
| result := GetTTFontFullNameFromStream(fs, aLanguageID); | |||
| finally | |||
| fs.Free; | |||
| end; | |||
| end; | |||
| end. | |||
| @@ -1,15 +1,15 @@ | |||
| unit utsTypes; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //Enumerations////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| {$Z4} | |||
| TtsCodePage = ( | |||
| tsUTF8, | |||
| @@ -58,12 +58,12 @@ type | |||
| tsISO_1257, | |||
| tsISO_1258); | |||
| TtsFontStyle = ( | |||
| tsStyleBold, | |||
| tsStyleItalic, | |||
| tsStyleUnderline, | |||
| tsStyleStrikeout); | |||
| TtsFontStyles = set of TtsFontStyle; | |||
| TtsFormat = ( | |||
| tsFormatEmpty, | |||
| tsFormatRGBA8, | |||
| tsFormatLumAlpha8, | |||
| tsFormatAlpha8, | |||
| tsFormatLum8); | |||
| TtsVertAlignment = ( | |||
| tsVertAlignTop, | |||
| @@ -76,17 +76,33 @@ type | |||
| tsHorzAlignRight, | |||
| tsHorzAlignJustify); | |||
| TtsFormat = ( | |||
| tsFormatEmpty, | |||
| tsFormatRGBA8, | |||
| tsFormatLumAlpha8, | |||
| tsFormatAlpha8, | |||
| tsFormatLum8); | |||
| TtsClipping = ( | |||
| tsClipNone, // no clipping | |||
| tsClipWordBorder, // draw all words that have at least one pixel inside the box | |||
| tsClipCharBorder, // draw all chars that have at least one pixel inside the box | |||
| tsClipWordComplete, // draw all words that are completly inside the box | |||
| tsClipCharComplete // draw all chars that are completly inside the box | |||
| ); | |||
| TtsAntiAliasing = ( | |||
| tsAANone, | |||
| tsAANormal); | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //Flags///////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsBlockFlag = ( | |||
| tsBlockFlagWordWrap | |||
| ); | |||
| TtsBlockFlags = set of TtsBlockFlag; | |||
| TtsFontStyle = ( | |||
| tsStyleBold, | |||
| tsStyleItalic, | |||
| tsStyleUnderline, | |||
| tsStyleStrikeout); | |||
| TtsFontStyles = set of TtsFontStyle; | |||
| TtsColorChannel = ( | |||
| tsChannelRed, | |||
| tsChannelGreen, | |||
| @@ -99,72 +115,42 @@ type | |||
| tsModeReplace, | |||
| tsModeModulate); | |||
| TtsImageModes = array[TtsColorChannel] of TtsImageMode; | |||
| TtsImageModeFunc = function(const aSource, aDest: Single): Single; | |||
| TtsFontProperties = packed record | |||
| Fontname: String; | |||
| Copyright: String; | |||
| FaceName: String; | |||
| StyleName: String; | |||
| FullName: String; | |||
| Size: Integer; | |||
| Style: TtsFontStyles; | |||
| AntiAliasing: TtsAntiAliasing; | |||
| DefaultChar: WideChar; | |||
| Ascent: Integer; | |||
| Descent: Integer; | |||
| ExternalLeading: Integer; | |||
| BaseLineOffset: Integer; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //Structures//////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsRenderRef = Pointer; | |||
| PtsCodePageValues = ^TtsCodePageValues; | |||
| TtsCodePageValues = array [AnsiChar] of word; | |||
| UnderlinePos: Integer; | |||
| UnderlineSize: Integer; | |||
| StrikeoutPos: Integer; | |||
| StrikeoutSize: Integer; | |||
| PtsColor4f = ^TtsColor4f; | |||
| TtsColor4f = packed record | |||
| case Boolean of | |||
| true: (r, g, b, a: Single); | |||
| false: (arr: array[0..3] of Single); | |||
| end; | |||
| PtsPosition = ^TtsPosition; | |||
| TtsPosition = packed record | |||
| x, y: Integer; | |||
| end; | |||
| PtsPosition = ^TtsPosition; | |||
| TtsPositionF = packed record | |||
| x, y: Single; | |||
| end; | |||
| PtsPositionF = ^TtsPositionF; | |||
| TtsRect = packed record | |||
| case Byte of | |||
| 0: (TopLeft: TtsPosition; BottomRight: TtsPosition); | |||
| 1: (Left, Top, Right, Bottom: Integer); | |||
| end; | |||
| PtsRect = ^TtsRect; | |||
| TtsRectF = packed record | |||
| case Byte of | |||
| 0: (TopLeft: TtsPositionF; BottomRight: TtsPositionF); | |||
| 1: (Left, Top, Right, Bottom: Single); | |||
| end; | |||
| PtsRectF = ^TtsRectF; | |||
| TtsColor4f = packed record | |||
| case Boolean of | |||
| true: (r, g, b, a: Single); | |||
| false: (arr: array[0..3] of Single); | |||
| end; | |||
| PtsColor4f = ^TtsColor4f; | |||
| TtsColor4ub = packed record | |||
| case Boolean of | |||
| true: (r, g, b, a: Byte); | |||
| false: (arr: array[0..3] of Byte); | |||
| TtsRect = packed record | |||
| case Byte of | |||
| 0: (TopLeft: TtsPosition; BottomRight: TtsPosition); | |||
| 1: (Left, Top, Right, Bottom: Integer); | |||
| end; | |||
| PtsColor4ub = ^TtsColor4ub; | |||
| TtsVector4f = array[0..3] of Single; | |||
| TtsMatrix4f = array[0..3] of TtsVector4f; | |||
| TtsGlyphMetric = packed record | |||
| GlyphOrigin: TtsPosition; | |||
| GlyphRect: TtsRect; | |||
| Advance: Integer; | |||
| end; | |||
| TtsTextMetric = packed record | |||
| Ascent: Integer; | |||
| Descent: Integer; | |||
| @@ -175,239 +161,36 @@ type | |||
| LineSpacing: Integer; | |||
| end; | |||
| TtsBlendFunc = function(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| const | |||
| TS_CHANNELS_RGB: TtsColorChannels = [tsChannelRed, tsChannelGreen, tsChannelBlue]; | |||
| TS_CHANNELS_RGBA: TtsColorChannels = [tsChannelRed, tsChannelGreen, tsChannelBlue, tsChannelAlpha]; | |||
| TS_MODES_REPLACE_ALL: TtsImageModes = (tsModeReplace, tsModeReplace, tsModeReplace, tsModeReplace); | |||
| TS_MODES_MODULATE_ALL: TtsImageModes = (tsModeModulate, tsModeModulate, tsModeModulate, tsModeModulate); | |||
| TS_MODES_MODULATE_ALPHA: TtsImageModes = (tsModeReplace, tsModeReplace, tsModeReplace, tsModeModulate); | |||
| TS_MATRIX_IDENTITY: TtsMatrix4f = ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)); | |||
| function tsColor4f(r, g, b, a: Single): TtsColor4f; | |||
| function tsModes(r, g, b, a: TtsImageMode): TtsImageModes; | |||
| function tsRect(const l, t, r, b: Integer): TtsRect; | |||
| function tsPosition(const x, y: Integer): TtsPosition; | |||
| function tsPositionF(const x, y: Single): TtsPositionF; | |||
| function tsVector4f(X, Y, Z, W: Single): TtsVector4f; | |||
| function tsMatrix4f(X, Y, Z, P: TtsVector4f): TtsMatrix4f; | |||
| function tsFormatSize(const aFormat: TtsFormat): Integer; | |||
| procedure tsFormatMap(const aFormat: TtsFormat; var aData: PByte; const aColor: TtsColor4f); | |||
| procedure tsFormatUnmap(const aFormat: TtsFormat; var aData: PByte; out aColor: TtsColor4f); | |||
| function tsImageModeFuncIgnore(const aSource, aDest: Single): Single; | |||
| function tsImageModeFuncReplace(const aSource, aDest: Single): Single; | |||
| function tsImageModeFuncModulate(const aSource, aDest: Single): Single; | |||
| function tsBlendFundAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| function tsBlendFundAdditive(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| function tsBlendFundAdditiveAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| implementation | |||
| uses | |||
| Math; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsColor4f(r, g, b, a: Single): TtsColor4f; | |||
| begin | |||
| result.r := r; | |||
| result.g := g; | |||
| result.b := b; | |||
| result.a := a; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsModes(r, g, b, a: TtsImageMode): TtsImageModes; | |||
| begin | |||
| result[tsChannelRed] := r; | |||
| result[tsChannelGreen] := g; | |||
| result[tsChannelBlue] := b; | |||
| result[tsChannelAlpha] := a; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsRect(const l, t, r, b: Integer): TtsRect; | |||
| begin | |||
| result.Left := l; | |||
| result.Top := t; | |||
| result.Right := r; | |||
| result.Bottom := b; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsPosition(const x, y: Integer): TtsPosition; | |||
| begin | |||
| result.x := x; | |||
| result.y := y; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsPositionF(const x, y: Single): TtsPositionF; | |||
| begin | |||
| result.x := x; | |||
| result.y := y; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsVector4f(X, Y, Z, W: Single): TtsVector4f; | |||
| begin | |||
| result[0] := X; | |||
| result[1] := Y; | |||
| result[2] := Z; | |||
| result[3] := W; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsMatrix4f(X, Y, Z, P: TtsVector4f): TtsMatrix4f; | |||
| begin | |||
| result[0] := X; | |||
| result[1] := Y; | |||
| result[2] := Z; | |||
| result[3] := P; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsFormatSize(const aFormat: TtsFormat): Integer; | |||
| begin | |||
| case aFormat of | |||
| tsFormatRGBA8: result := 4; | |||
| tsFormatLumAlpha8: result := 2; | |||
| tsFormatAlpha8: result := 1; | |||
| tsFormatLum8: result := 1; | |||
| else | |||
| result := 0; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure tsFormatMap(const aFormat: TtsFormat; var aData: PByte; const aColor: TtsColor4f); | |||
| var | |||
| i: Integer; | |||
| s: Single; | |||
| begin | |||
| case aFormat of | |||
| tsFormatRGBA8: begin | |||
| for i := 0 to 3 do begin | |||
| aData^ := Trunc($FF * min(aColor.arr[i], 1.0)); | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| tsFormatLumAlpha8: begin | |||
| s := 0.30 * min(aColor.r, 1.0) + | |||
| 0.59 * min(aColor.g, 1.0) + | |||
| 0.11 * min(aColor.b, 1.0); | |||
| aData^ := Trunc($FF * s); | |||
| inc(aData); | |||
| aData^ := Trunc($FF * min(aColor.a, 1.0)); | |||
| inc(aData); | |||
| end; | |||
| tsFormatAlpha8: begin | |||
| aData^ := Trunc($FF * min(aColor.a, 1.0)); | |||
| inc(aData); | |||
| end; | |||
| tsFormatLum8: begin | |||
| s := 0.30 * min(aColor.r, 1.0) + | |||
| 0.59 * min(aColor.g, 1.0) + | |||
| 0.11 * min(aColor.b, 1.0); | |||
| aData^ := Trunc($FF * s); | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure tsFormatUnmap(const aFormat: TtsFormat; var aData: PByte; out aColor: TtsColor4f); | |||
| var | |||
| i: Integer; | |||
| begin | |||
| case aFormat of | |||
| tsFormatRGBA8: begin | |||
| for i := 0 to 3 do begin | |||
| aColor.arr[i] := aData^ / $FF; | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| TtsFontMetric = packed record | |||
| Fontname: String; | |||
| Copyright: String; | |||
| FaceName: String; | |||
| StyleName: String; | |||
| FullName: String; | |||
| tsFormatLumAlpha8: begin | |||
| aColor.r := aData^ / $FF; | |||
| aColor.g := aData^ / $FF; | |||
| aColor.b := aData^ / $FF; | |||
| inc(aData); | |||
| aColor.a := aData^ / $FF; | |||
| inc(aData); | |||
| end; | |||
| Size: Integer; | |||
| Style: TtsFontStyles; | |||
| AntiAliasing: TtsAntiAliasing; | |||
| DefaultChar: WideChar; | |||
| tsFormatAlpha8: begin | |||
| aColor.r := 1.0; | |||
| aColor.g := 1.0; | |||
| aColor.b := 1.0; | |||
| aColor.a := aData^ / $FF; | |||
| inc(aData); | |||
| end; | |||
| Ascent: Integer; | |||
| Descent: Integer; | |||
| ExternalLeading: Integer; | |||
| BaseLineOffset: Integer; | |||
| tsFormatLum8: begin | |||
| aColor.r := aData^ / $FF; | |||
| aColor.g := aData^ / $FF; | |||
| aColor.b := aData^ / $FF; | |||
| aColor.a := 1.0; | |||
| inc(aData); | |||
| end; | |||
| UnderlinePos: Integer; | |||
| UnderlineSize: Integer; | |||
| StrikeoutPos: Integer; | |||
| StrikeoutSize: Integer; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsImageModeFuncIgnore(const aSource, aDest: Single): Single; | |||
| begin | |||
| result := aDest; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsImageModeFuncReplace(const aSource, aDest: Single): Single; | |||
| begin | |||
| result := aSource; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsImageModeFuncModulate(const aSource, aDest: Single): Single; | |||
| begin | |||
| result := aSource * aDest; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendFundAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to 2 do | |||
| result.arr[i] := aSrc.arr[i] * aSrc.a + aDst.arr[i] * (1 - aSrc.a); | |||
| result.a := aSrc.a + aDst.a * (1 - aSrc.a); | |||
| end; | |||
| //Callbacks///////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendFundAdditive(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to 3 do | |||
| result.arr[i] := aSrc.arr[i] + aDst.arr[i]; | |||
| end; | |||
| TtsBlendValueFunc = function(const aSrc, aDst: Single): Single; | |||
| TtsBlendColorFunc = function(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| function tsBlendFundAdditiveAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to 2 do | |||
| result.arr[i] := aSrc.arr[i] * aSrc.a + aDst.arr[i]; | |||
| result.a := aDst.a; | |||
| end; | |||
| implementation | |||
| end. | |||
| @@ -1,30 +1,386 @@ | |||
| unit utsUtils; | |||
| {$IFDEF FPC} | |||
| {$mode delphi}{$H+} | |||
| {$mode objfpc}{$H+} | |||
| {$ENDIF} | |||
| interface | |||
| uses | |||
| Classes, SysUtils, utsTypes; | |||
| Classes, SysUtils, Contnrs, | |||
| utsTypes; | |||
| function tsStrAlloc(aSize: Cardinal): PWideChar; | |||
| function tsStrNew(const aText: PWideChar): PWideChar; | |||
| procedure tsStrDispose(const aText: PWideChar); | |||
| function tsStrLength(aText: PWideChar): Cardinal; | |||
| function tsStrCopy(aDst, aSrc: PWideChar): PWideChar; | |||
| type | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsRefManager = class(TObject) | |||
| private | |||
| fMasterRef: TtsRefManager; | |||
| fSlaveRefs: TObjectList; | |||
| protected | |||
| procedure AddSlave(const aSlave: TtsRefManager); virtual; | |||
| procedure DelSlave(const aSlave: TtsRefManager); virtual; | |||
| public | |||
| constructor Create(const aMaster: TtsRefManager); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsMultiMasterRefManager = class(TtsRefManager) | |||
| private | |||
| fMasterRefs: TObjectList; | |||
| public | |||
| procedure AddMaster(const aMaster: TtsRefManager); virtual; | |||
| procedure DelMaster(const aMaster: TtsRefManager); virtual; | |||
| constructor Create(const aMaster: TtsRefManager); | |||
| destructor Destroy; override; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsKernel1DItem = packed record | |||
| Offset: Integer; | |||
| Value: Single; | |||
| end; | |||
| TtsKernel1D = class | |||
| public | |||
| Size: Integer; | |||
| Items: array of TtsKernel1DItem; | |||
| ItemCount: Integer; | |||
| constructor Create(const aRadius, aStrength: Single); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| TtsKernel2DItem = packed record | |||
| OffsetX: Integer; | |||
| OffsetY: Integer; | |||
| Value: Double; | |||
| DataOffset: Integer; | |||
| end; | |||
| TtsKernel2D = class | |||
| public | |||
| SizeX: Integer; | |||
| SizeY: Integer; | |||
| MidSizeX: Integer; | |||
| MidSizeY: Integer; | |||
| ValueSum: Double; | |||
| Items: array of TtsKernel2DItem; | |||
| ItemCount: Integer; | |||
| constructor Create(const aRadius, aStrength: Single); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| EtsException = class(Exception); | |||
| EtsRenderer = class(EtsException); | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsColor4f(r, g, b, a: Single): TtsColor4f; | |||
| function tsPosition(const x, y: Integer): TtsPosition; | |||
| function tsRect(const l, t, r, b: Integer): TtsRect; overload; | |||
| function tsRect(const aTopLeft, aBottomRight: TtsPosition): TtsRect; overload; | |||
| function tsVector4f(X, Y, Z, W: Single): TtsVector4f; | |||
| function tsMatrix4f(X, Y, Z, P: TtsVector4f): TtsMatrix4f; | |||
| function tsFormatSize(const aFormat: TtsFormat): Integer; | |||
| procedure tsFormatMap(const aFormat: TtsFormat; var aData: PByte; const aColor: TtsColor4f); | |||
| procedure tsFormatUnmap(const aFormat: TtsFormat; var aData: PByte; out aColor: TtsColor4f); | |||
| function tsBlendValueIgnore(const aSrc, aDst: Single): Single; | |||
| function tsBlendValueReplace(const aSrc, aDst: Single): Single; | |||
| function tsBlendValueModulate(const aSrc, aDst: Single): Single; | |||
| function tsAnsiToWide(aDst: PWideChar; const aSize: Integer; aSrc: PAnsiChar; const aCodePage: TtsCodePage; const aDefaultChar: WideChar): Integer; | |||
| function tsISO_8859_1ToWide(aDst: PWideChar; const aSize: Integer; aSrc: PAnsiChar): Integer; | |||
| function tsUTF8ToWide(aDst: PWideChar; const aSize: Integer; const aSrc: PAnsiChar; const aDefaultChar: WideChar): Integer; | |||
| function tsUTFBE16ToWide(aDst: PWideChar; const aDstSize: Integer; aSrc: PByte; aSrcSize: Integer; const aDefaultChar: WideChar): Integer; | |||
| function tsAnsiSBCDToWide(aDst: PWideChar; const aSize: Integer; aSrc: PAnsiChar; const aCodePage: TtsCodePage; const aDefaultChar: WideChar): Integer; | |||
| function tsBlendColorAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| function tsBlendColorAdditive(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| function tsBlendColorAdditiveAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| function tsStrAlloc(aSize: Cardinal): PWideChar; | |||
| function tsStrNew(const aText: PWideChar): PWideChar; | |||
| procedure tsStrDispose(const aText: PWideChar); | |||
| function tsStrLength(aText: PWideChar): Cardinal; | |||
| function tsStrCopy(aDst, aSrc: PWideChar): PWideChar; | |||
| function tsAnsiToWide(aDst: PWideChar; const aSize: Integer; aSrc: PAnsiChar; const aCodePage: TtsCodePage; const aDefaultChar: WideChar): Integer; | |||
| function tsISO_8859_1ToWide(aDst: PWideChar; const aSize: Integer; aSrc: PAnsiChar): Integer; | |||
| function tsUTF8ToWide(aDst: PWideChar; const aSize: Integer; const aSrc: PAnsiChar; const aDefaultChar: WideChar): Integer; | |||
| function tsUTFBE16ToWide(aDst: PWideChar; const aDstSize: Integer; aSrc: PByte; aSrcSize: Integer; const aDefaultChar: WideChar): Integer; | |||
| function tsAnsiSBCDToWide(aDst: PWideChar; const aSize: Integer; aSrc: PAnsiChar; const aCodePage: TtsCodePage; const aDefaultChar: WideChar): Integer; | |||
| implementation | |||
| uses | |||
| utsCodePages; | |||
| math, | |||
| utsConstants; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsRefManager///////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRefManager.AddSlave(const aSlave: TtsRefManager); | |||
| begin | |||
| if Assigned(fSlaveRefs) then | |||
| fSlaveRefs.Add(aSlave); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsRefManager.DelSlave(const aSlave: TtsRefManager); | |||
| begin | |||
| if Assigned(fSlaveRefs) then | |||
| fSlaveRefs.Remove(aSlave); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsRefManager.Create(const aMaster: TtsRefManager); | |||
| begin | |||
| inherited Create; | |||
| fMasterRef := aMaster; | |||
| fSlaveRefs := TObjectList.Create(false); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsRefManager.Destroy; | |||
| var | |||
| m: TtsRefManager; | |||
| begin | |||
| fSlaveRefs.OwnsObjects := true; | |||
| FreeAndNil(fSlaveRefs); | |||
| m := fMasterRef; | |||
| fMasterRef := nil; | |||
| if Assigned(m) then | |||
| m.DelSlave(self); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsMultiMasterRefManager////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsMultiMasterRefManager.AddMaster(const aMaster: TtsRefManager); | |||
| begin | |||
| if Assigned(fMasterRefs) then begin | |||
| fMasterRefs.Add(aMaster); | |||
| aMaster.AddSlave(self); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure TtsMultiMasterRefManager.DelMaster(const aMaster: TtsRefManager); | |||
| begin | |||
| if Assigned(fMasterRefs) then begin | |||
| if (fMasterRefs.Remove(aMaster) >= 0) then | |||
| aMaster.DelSlave(self); | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsMultiMasterRefManager.Create(const aMaster: TtsRefManager); | |||
| begin | |||
| inherited Create(aMaster); | |||
| fMasterRefs := TObjectList.Create(false); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| destructor TtsMultiMasterRefManager.Destroy; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := fMasterRefs.Count-1 downto 0 do | |||
| DelMaster(fMasterRefs[i] as TtsRefManager); | |||
| inherited Destroy; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //Helper Methods//////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsColor4f(r, g, b, a: Single): TtsColor4f; | |||
| begin | |||
| result.r := r; | |||
| result.g := g; | |||
| result.b := b; | |||
| result.a := a; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsPosition(const x, y: Integer): TtsPosition; | |||
| begin | |||
| result.x := x; | |||
| result.y := y; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsRect(const l, t, r, b: Integer): TtsRect; | |||
| begin | |||
| result.Left := l; | |||
| result.Top := t; | |||
| result.Right := r; | |||
| result.Bottom := b; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsRect(const aTopLeft, aBottomRight: TtsPosition): TtsRect; | |||
| begin | |||
| result.TopLeft := aTopLeft; | |||
| result.BottomRight := aBottomRight; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsVector4f(X, Y, Z, W: Single): TtsVector4f; | |||
| begin | |||
| result[0] := X; | |||
| result[1] := Y; | |||
| result[2] := Z; | |||
| result[3] := W; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsMatrix4f(X, Y, Z, P: TtsVector4f): TtsMatrix4f; | |||
| begin | |||
| result[0] := X; | |||
| result[1] := Y; | |||
| result[2] := Z; | |||
| result[3] := P; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsFormatSize(const aFormat: TtsFormat): Integer; | |||
| begin | |||
| case aFormat of | |||
| tsFormatRGBA8: result := 4; | |||
| tsFormatLumAlpha8: result := 2; | |||
| tsFormatAlpha8: result := 1; | |||
| tsFormatLum8: result := 1; | |||
| else | |||
| result := 0; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure tsFormatMap(const aFormat: TtsFormat; var aData: PByte; const aColor: TtsColor4f); | |||
| var | |||
| i: Integer; | |||
| s: Single; | |||
| begin | |||
| case aFormat of | |||
| tsFormatRGBA8: begin | |||
| for i := 0 to 3 do begin | |||
| aData^ := Trunc($FF * min(aColor.arr[i], 1.0)); | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| tsFormatLumAlpha8: begin | |||
| s := 0.30 * min(aColor.r, 1.0) + | |||
| 0.59 * min(aColor.g, 1.0) + | |||
| 0.11 * min(aColor.b, 1.0); | |||
| aData^ := Trunc($FF * s); | |||
| inc(aData); | |||
| aData^ := Trunc($FF * min(aColor.a, 1.0)); | |||
| inc(aData); | |||
| end; | |||
| tsFormatAlpha8: begin | |||
| aData^ := Trunc($FF * min(aColor.a, 1.0)); | |||
| inc(aData); | |||
| end; | |||
| tsFormatLum8: begin | |||
| s := 0.30 * min(aColor.r, 1.0) + | |||
| 0.59 * min(aColor.g, 1.0) + | |||
| 0.11 * min(aColor.b, 1.0); | |||
| aData^ := Trunc($FF * s); | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| procedure tsFormatUnmap(const aFormat: TtsFormat; var aData: PByte; out aColor: TtsColor4f); | |||
| var | |||
| i: Integer; | |||
| begin | |||
| case aFormat of | |||
| tsFormatRGBA8: begin | |||
| for i := 0 to 3 do begin | |||
| aColor.arr[i] := aData^ / $FF; | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| tsFormatLumAlpha8: begin | |||
| aColor.r := aData^ / $FF; | |||
| aColor.g := aData^ / $FF; | |||
| aColor.b := aData^ / $FF; | |||
| inc(aData); | |||
| aColor.a := aData^ / $FF; | |||
| inc(aData); | |||
| end; | |||
| tsFormatAlpha8: begin | |||
| aColor.r := 1.0; | |||
| aColor.g := 1.0; | |||
| aColor.b := 1.0; | |||
| aColor.a := aData^ / $FF; | |||
| inc(aData); | |||
| end; | |||
| tsFormatLum8: begin | |||
| aColor.r := aData^ / $FF; | |||
| aColor.g := aData^ / $FF; | |||
| aColor.b := aData^ / $FF; | |||
| aColor.a := 1.0; | |||
| inc(aData); | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendValueIgnore(const aSrc, aDst: Single): Single; | |||
| begin | |||
| result := aDst; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendValueReplace(const aSrc, aDst: Single): Single; | |||
| begin | |||
| result := aSrc; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendValueModulate(const aSrc, aDst: Single): Single; | |||
| begin | |||
| result := aSrc * aDst; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendColorAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to 2 do | |||
| result.arr[i] := aSrc.arr[i] * aSrc.a + aDst.arr[i] * (1 - aSrc.a); | |||
| result.a := aSrc.a + aDst.a * (1 - aSrc.a); | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendColorAdditive(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to 3 do | |||
| result.arr[i] := aSrc.arr[i] + aDst.arr[i]; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsBlendColorAdditiveAlpha(const aSrc, aDst: TtsColor4f): TtsColor4f; | |||
| var | |||
| i: Integer; | |||
| begin | |||
| for i := 0 to 2 do | |||
| result.arr[i] := aSrc.arr[i] * aSrc.a + aDst.arr[i]; | |||
| result.a := aDst.a; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| function tsStrAlloc(aSize: Cardinal): PWideChar; | |||
| @@ -205,7 +561,7 @@ var | |||
| cp: PtsCodePageValues; | |||
| begin | |||
| result := 0; | |||
| cp := ANSI_TO_WIDE_CODE_PAGE_LUT[aCodePage]; | |||
| cp := TS_CODE_PAGE_LUT[aCodePage]; | |||
| if not Assigned(aDst) or | |||
| not Assigned(aSrc) or | |||
| not Assigned(cp) or | |||
| @@ -226,5 +582,164 @@ begin | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsKernel1D/////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsKernel1D.Create(const aRadius, aStrength: Single); | |||
| var | |||
| TempRadius, SQRRadius, TempStrength, TempValue: Double; | |||
| Idx: Integer; | |||
| function CalcValue(const aIndex: Integer): Single; | |||
| var | |||
| Temp: Double; | |||
| begin | |||
| Temp := Max(0, Abs(aIndex) - TempStrength); | |||
| Temp := Sqr(Temp * TempRadius) / SQRRadius; | |||
| result := Exp(-Temp); | |||
| end; | |||
| begin | |||
| inherited Create; | |||
| // calculate new radius and strength | |||
| TempStrength := Min(aRadius - 1, aRadius * aStrength); | |||
| TempRadius := aRadius - TempStrength; | |||
| SQRRadius := sqr(TempRadius) * sqr(TempRadius); | |||
| // caluculating size of the kernel | |||
| Size := Round(TempRadius); | |||
| while CalcValue(Size) > 0.001 do | |||
| Inc(Size); | |||
| Size := Size -1; | |||
| ItemCount := Size * 2 +1; | |||
| SetLength(Items, ItemCount); | |||
| // calculate Value (yes thats right. there is no -1) | |||
| for Idx := 0 to Size do begin | |||
| TempValue := CalcValue(Idx); | |||
| with Items[Size + Idx] do begin | |||
| Offset := Idx; | |||
| Value := TempValue; | |||
| end; | |||
| with Items[Size - Idx] do begin | |||
| Offset := -Idx; | |||
| Value := TempValue; | |||
| end; | |||
| end; | |||
| end; | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //TtsKernel2D/////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||
| constructor TtsKernel2D.Create(const aRadius, aStrength: Single); | |||
| var | |||
| tmpStrenght: Double; | |||
| tmpRadius: Double; | |||
| tmpValue: Double; | |||
| sqrRadius: Double; | |||
| x, y, w, h: Integer; | |||
| function CalcValue(const aIndex: Double): Double; | |||
| begin | |||
| result := max(0, Abs(aIndex) - tmpStrenght); | |||
| result := Sqr(result * tmpRadius) / sqrRadius; | |||
| result := Exp(-result); | |||
| end; | |||
| procedure CalcSize(var aSize, aMidSize: Integer); | |||
| begin | |||
| aSize := 0; | |||
| aMidSize := 0; | |||
| while CalcValue(aSize) > 0.5 do begin | |||
| inc(aSize, 1); | |||
| inc(aMidSize, 1); | |||
| end; | |||
| while CalcValue(aSize) > 0.001 do | |||
| Inc(aSize, 1); | |||
| end; | |||
| procedure SetItem(const x, y: Integer); | |||
| begin | |||
| with Items[(SizeY + y) * w + (SizeX + x)] do begin | |||
| OffsetX := x; | |||
| OffsetY := y; | |||
| Value := tmpValue; | |||
| end; | |||
| end; | |||
| procedure QuickSort(l, r: Integer); | |||
| var | |||
| _l, _r: Integer; | |||
| p, t: TtsKernel2DItem; | |||
| begin | |||
| repeat | |||
| _l := l; | |||
| _r := r; | |||
| p := Items[(l + r) shr 1]; | |||
| repeat | |||
| while (Items[_l].Value > p.Value) do | |||
| inc(_l, 1); | |||
| while (Items[_r].Value < p.Value) do | |||
| dec(_r, 1); | |||
| if (_l <= _r) then begin | |||
| t := Items[_l]; | |||
| Items[_l] := Items[_r]; | |||
| Items[_r] := t; | |||
| inc(_l, 1); | |||
| dec(_r, 1); | |||
| end; | |||
| until (_l > _r); | |||
| if (l < _r) then | |||
| QuickSort(l, _r); | |||
| l := _l; | |||
| until (_l >= r); | |||
| end; | |||
| begin | |||
| inherited Create; | |||
| tmpStrenght := Min(aRadius - 1.0, aRadius * aStrength); | |||
| tmpRadius := aRadius - tmpStrenght; | |||
| sqrRadius := sqr(tmpRadius) * sqr(tmpRadius); | |||
| CalcSize(SizeX, MidSizeX); | |||
| CalcSize(SizeY, MidSizeY); | |||
| ValueSum := 0.0; | |||
| w := 2 * SizeX + 1; | |||
| h := 2 * SizeY + 1; | |||
| ItemCount := w * h; | |||
| SetLength(Items, ItemCount); | |||
| for y := 0 to SizeY do begin | |||
| for x := 0 to SizeX do begin | |||
| tmpValue := CalcValue(sqrt(Sqr(x) + Sqr(y))); | |||
| SetItem( x, y); | |||
| SetItem( x, -y); | |||
| SetItem(-x, -y); | |||
| SetItem(-x, y); | |||
| ValueSum := ValueSum + tmpValue; | |||
| if (x > 0) and (y > 0) then | |||
| ValueSum := ValueSum + tmpValue; | |||
| end; | |||
| end; | |||
| QuickSort(0, ItemCount-1); | |||
| while (Items[ItemCount-1].Value < 0.001) do | |||
| dec(ItemCount, 1); | |||
| SetLength(Items, ItemCount); | |||
| end; | |||
| end. | |||