| @@ -134,5 +134,6 @@ Comments= | |||||
| Count=1 | Count=1 | ||||
| Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; | Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; | ||||
| [HistoryLists\hlSearchPath] | [HistoryLists\hlSearchPath] | ||||
| Count=1 | |||||
| Count=2 | |||||
| Item0=..\utils;..\.. | Item0=..\utils;..\.. | ||||
| Item1=..\utils;..\..;..\..\inc | |||||
| @@ -5,7 +5,7 @@ interface | |||||
| uses | uses | ||||
| Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, | Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, | ||||
| uglcContext, | uglcContext, | ||||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorGDI, utsTypes, utsPostProcess; | |||||
| utsTextSuite, utsTypes, utsUtils, utsConstants, utsPostProcessor; | |||||
| type | type | ||||
| TMainForm = class(TForm) | TMainForm = class(TForm) | ||||
| @@ -16,8 +16,9 @@ type | |||||
| fContext: TglcContext; | fContext: TglcContext; | ||||
| ftsContext: TtsContext; | ftsContext: TtsContext; | ||||
| ftsRenderer: TtsRendererOpenGL; | ftsRenderer: TtsRendererOpenGL; | ||||
| ftsCreator1: TtsFontGeneratorGDI; | |||||
| ftsCreator2: TtsFontGeneratorGDI; | |||||
| ftsCreator: TtsFontCreatorGDI; | |||||
| ftsPostProcessList1: TtsPostProcessorList; | |||||
| ftsPostProcessList2: TtsPostProcessorList; | |||||
| ftsFont1: TtsFont; | ftsFont1: TtsFont; | ||||
| ftsFont2: TtsFont; | ftsFont2: TtsFont; | ||||
| procedure Render; | procedure Render; | ||||
| @@ -41,7 +42,7 @@ const | |||||
| procedure TMainForm.FormCreate(Sender: TObject); | procedure TMainForm.FormCreate(Sender: TObject); | ||||
| var | var | ||||
| pf: TglcContextPixelFormatSettings; | pf: TglcContextPixelFormatSettings; | ||||
| pp: TtsPostProcessStep; | |||||
| pp: TtsPostProcessor; | |||||
| img: TtsImage; | img: TtsImage; | ||||
| const | const | ||||
| @@ -59,42 +60,50 @@ begin | |||||
| ftsContext := TtsContext.Create; | ftsContext := TtsContext.Create; | ||||
| ftsRenderer := TtsRendererOpenGL.Create(ftsContext, tsFormatRGBA8); | 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 := TtsImage.Create; | ||||
| img.CreateEmpty(tsFormatAlpha8, 4, 4); | img.CreateEmpty(tsFormatAlpha8, 4, 4); | ||||
| Move(PATTER_DATA[0], img.Data^, 16); | 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; | end; | ||||
| procedure TMainForm.FormDestroy(Sender: TObject); | procedure TMainForm.FormDestroy(Sender: TObject); | ||||
| begin | begin | ||||
| FreeAndNil(ftsFont2); | FreeAndNil(ftsFont2); | ||||
| FreeAndNil(ftsCreator2); | |||||
| FreeAndNil(ftsFont1); | |||||
| FreeAndNil(ftsCreator1); | |||||
| FreeAndNil(ftsFont1); | |||||
| FreeAndNil(ftsCreator); | |||||
| FreeAndNil(ftsPostProcessList2); | |||||
| FreeAndNil(ftsPostProcessList1); | |||||
| FreeAndNil(ftsRenderer); | FreeAndNil(ftsRenderer); | ||||
| FreeAndNil(ftsContext); | FreeAndNil(ftsContext); | ||||
| FreeAndNil(fContext); | 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"/> | <ComponentName Value="MainForm"/> | ||||
| <HasResources Value="True"/> | <HasResources Value="True"/> | ||||
| <ResourceBaseClass Value="Form"/> | <ResourceBaseClass Value="Form"/> | ||||
| <UnitName Value="uMainForm"/> | |||||
| </Unit1> | </Unit1> | ||||
| </Units> | </Units> | ||||
| </ProjectOptions> | </ProjectOptions> | ||||
| @@ -54,7 +53,7 @@ | |||||
| <Filename Value="PostProcess"/> | <Filename Value="PostProcess"/> | ||||
| </Target> | </Target> | ||||
| <SearchPaths> | <SearchPaths> | ||||
| <IncludeFiles Value="$(ProjOutDir)"/> | |||||
| <IncludeFiles Value="$(ProjOutDir);..\..\inc"/> | |||||
| <OtherUnitFiles Value="..\utils;..\.."/> | <OtherUnitFiles Value="..\utils;..\.."/> | ||||
| <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | ||||
| </SearchPaths> | </SearchPaths> | ||||
| @@ -7,7 +7,7 @@ interface | |||||
| uses | uses | ||||
| Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | ||||
| uglcContext, | uglcContext, | ||||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorGDI, utsTypes, utsPostProcess; | |||||
| utsTextSuite, utsUtils, utsConstants; | |||||
| type | type | ||||
| TMainForm = class(TForm) | TMainForm = class(TForm) | ||||
| @@ -18,8 +18,9 @@ type | |||||
| fContext: TglcContext; | fContext: TglcContext; | ||||
| ftsContext: TtsContext; | ftsContext: TtsContext; | ||||
| ftsRenderer: TtsRendererOpenGL; | ftsRenderer: TtsRendererOpenGL; | ||||
| ftsCreator1: TtsFontGeneratorGDI; | |||||
| ftsCreator2: TtsFontGeneratorGDI; | |||||
| ftsCreator: TtsFontCreatorGDI; | |||||
| ftsPostProcessor1: TtsPostProcessorList; | |||||
| ftsPostProcessor2: TtsPostProcessorList; | |||||
| ftsFont1: TtsFont; | ftsFont1: TtsFont; | ||||
| ftsFont2: TtsFont; | ftsFont2: TtsFont; | ||||
| procedure Render; | procedure Render; | ||||
| @@ -43,7 +44,7 @@ const | |||||
| procedure TMainForm.FormCreate(Sender: TObject); | procedure TMainForm.FormCreate(Sender: TObject); | ||||
| var | var | ||||
| pf: TglcContextPixelFormatSettings; | pf: TglcContextPixelFormatSettings; | ||||
| pp: TtsPostProcessStep; | |||||
| pp: TtsPostProcessor; | |||||
| img: TtsImage; | img: TtsImage; | ||||
| const | const | ||||
| @@ -59,44 +60,52 @@ begin | |||||
| fContext.BuildContext; | fContext.BuildContext; | ||||
| ftsContext := TtsContext.Create; | 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 := TtsImage.Create; | ||||
| img.CreateEmpty(tsFormatAlpha8, 4, 4); | |||||
| img.CreateEmpty(TtsFormat.tsFormatAlpha8, 4, 4); | |||||
| Move(PATTER_DATA[0], img.Data^, 16); | 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; | end; | ||||
| procedure TMainForm.FormDestroy(Sender: TObject); | procedure TMainForm.FormDestroy(Sender: TObject); | ||||
| begin | begin | ||||
| FreeAndNil(ftsFont2); | FreeAndNil(ftsFont2); | ||||
| FreeAndNil(ftsCreator2); | |||||
| FreeAndNil(ftsFont1); | FreeAndNil(ftsFont1); | ||||
| FreeAndNil(ftsCreator1); | |||||
| FreeAndNil(ftsCreator); | |||||
| FreeAndNil(ftsPostProcessor2); | |||||
| FreeAndNil(ftsPostProcessor1); | |||||
| FreeAndNil(ftsRenderer); | FreeAndNil(ftsRenderer); | ||||
| FreeAndNil(ftsContext); | FreeAndNil(ftsContext); | ||||
| FreeAndNil(fContext); | FreeAndNil(fContext); | ||||
| @@ -127,9 +136,9 @@ begin | |||||
| glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 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 | try | ||||
| block.HorzAlign := tsHorzAlignJustify; | |||||
| block.HorzAlign := TtsHorzAlignment.tsHorzAlignJustify; | |||||
| block.ChangeFont(ftsFont1); | block.ChangeFont(ftsFont1); | ||||
| block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | ||||
| block.TextOutW(TEST_TEXT + sLineBreak); | block.TextOutW(TEST_TEXT + sLineBreak); | ||||
| @@ -43,6 +43,7 @@ | |||||
| <ComponentName Value="MainForm"/> | <ComponentName Value="MainForm"/> | ||||
| <HasResources Value="True"/> | <HasResources Value="True"/> | ||||
| <ResourceBaseClass Value="Form"/> | <ResourceBaseClass Value="Form"/> | ||||
| <UnitName Value="uMainForm"/> | |||||
| </Unit1> | </Unit1> | ||||
| </Units> | </Units> | ||||
| </ProjectOptions> | </ProjectOptions> | ||||
| @@ -53,7 +54,7 @@ | |||||
| <Filename Value="SimpleFreeType"/> | <Filename Value="SimpleFreeType"/> | ||||
| </Target> | </Target> | ||||
| <SearchPaths> | <SearchPaths> | ||||
| <IncludeFiles Value="$(ProjOutDir)"/> | |||||
| <IncludeFiles Value="$(ProjOutDir);..\..\inc"/> | |||||
| <OtherUnitFiles Value="..\utils;..\.."/> | <OtherUnitFiles Value="..\utils;..\.."/> | ||||
| <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | ||||
| </SearchPaths> | </SearchPaths> | ||||
| @@ -7,7 +7,7 @@ interface | |||||
| uses | uses | ||||
| Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | ||||
| uglcContext, | uglcContext, | ||||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorFreeType, utsTypes; | |||||
| utsTextSuite, utsUtils; | |||||
| type | type | ||||
| TMainForm = class(TForm) | TMainForm = class(TForm) | ||||
| @@ -18,7 +18,7 @@ type | |||||
| fContext: TglcContext; | fContext: TglcContext; | ||||
| ftsContext: TtsContext; | ftsContext: TtsContext; | ||||
| ftsRenderer: TtsRendererOpenGL; | ftsRenderer: TtsRendererOpenGL; | ||||
| ftsCreator: TtsFontGeneratorFreeType; | |||||
| ftsCreator: TtsFontCreatorFreeType; | |||||
| ftsFont: TtsFont; | ftsFont: TtsFont; | ||||
| procedure Render; | procedure Render; | ||||
| public | public | ||||
| @@ -47,9 +47,9 @@ begin | |||||
| fContext.BuildContext; | fContext.BuildContext; | ||||
| ftsContext := TtsContext.Create; | 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; | end; | ||||
| procedure TMainForm.FormDestroy(Sender: TObject); | procedure TMainForm.FormDestroy(Sender: TObject); | ||||
| @@ -86,9 +86,9 @@ begin | |||||
| glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 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 | try | ||||
| block.HorzAlign := tsHorzAlignJustify; | |||||
| block.HorzAlign := TtsHorzAlignment.tsHorzAlignJustify; | |||||
| block.ChangeFont(ftsFont); | block.ChangeFont(ftsFont); | ||||
| block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | ||||
| block.TextOutW(TEST_TEXT); | block.TextOutW(TEST_TEXT); | ||||
| @@ -32,7 +32,7 @@ | |||||
| <PackageName Value="LCL"/> | <PackageName Value="LCL"/> | ||||
| </Item1> | </Item1> | ||||
| </RequiredPackages> | </RequiredPackages> | ||||
| <Units Count="2"> | |||||
| <Units Count="21"> | |||||
| <Unit0> | <Unit0> | ||||
| <Filename Value="SimpleGDI.lpr"/> | <Filename Value="SimpleGDI.lpr"/> | ||||
| <IsPartOfProject Value="True"/> | <IsPartOfProject Value="True"/> | ||||
| @@ -43,7 +43,103 @@ | |||||
| <ComponentName Value="MainForm"/> | <ComponentName Value="MainForm"/> | ||||
| <HasResources Value="True"/> | <HasResources Value="True"/> | ||||
| <ResourceBaseClass Value="Form"/> | <ResourceBaseClass Value="Form"/> | ||||
| <UnitName Value="uMainForm"/> | |||||
| </Unit1> | </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> | </Units> | ||||
| </ProjectOptions> | </ProjectOptions> | ||||
| <CompilerOptions> | <CompilerOptions> | ||||
| @@ -53,7 +149,7 @@ | |||||
| <Filename Value="SimpleGDI"/> | <Filename Value="SimpleGDI"/> | ||||
| </Target> | </Target> | ||||
| <SearchPaths> | <SearchPaths> | ||||
| <IncludeFiles Value="$(ProjOutDir)"/> | |||||
| <IncludeFiles Value="$(ProjOutDir);..\..\inc"/> | |||||
| <OtherUnitFiles Value="..\utils;..\.."/> | <OtherUnitFiles Value="..\utils;..\.."/> | ||||
| <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> | ||||
| </SearchPaths> | </SearchPaths> | ||||
| @@ -64,6 +160,11 @@ | |||||
| </Win32> | </Win32> | ||||
| </Options> | </Options> | ||||
| </Linking> | </Linking> | ||||
| <Other> | |||||
| <CompilerMessages> | |||||
| <IgnoredMessages idx5024="True"/> | |||||
| </CompilerMessages> | |||||
| </Other> | |||||
| </CompilerOptions> | </CompilerOptions> | ||||
| <Debugging> | <Debugging> | ||||
| <Exceptions Count="3"> | <Exceptions Count="3"> | ||||
| @@ -7,8 +7,7 @@ uses | |||||
| cthreads, | cthreads, | ||||
| {$ENDIF}{$ENDIF} | {$ENDIF}{$ENDIF} | ||||
| Interfaces, // this includes the LCL widgetset | Interfaces, // this includes the LCL widgetset | ||||
| Forms, uMainForm | |||||
| { you can add units after this }; | |||||
| Forms, uMainForm; | |||||
| {$R *.res} | {$R *.res} | ||||
| @@ -6,8 +6,8 @@ interface | |||||
| uses | uses | ||||
| Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, | ||||
| uglcContext, | |||||
| utsTextSuite, utsRendererOpenGL, utsFontCreatorGDI, utsTypes; | |||||
| uglcContext, utsUtils, | |||||
| utsTextSuite; | |||||
| type | type | ||||
| TMainForm = class(TForm) | TMainForm = class(TForm) | ||||
| @@ -18,7 +18,7 @@ type | |||||
| fContext: TglcContext; | fContext: TglcContext; | ||||
| ftsContext: TtsContext; | ftsContext: TtsContext; | ||||
| ftsRenderer: TtsRendererOpenGL; | ftsRenderer: TtsRendererOpenGL; | ||||
| ftsCreator: TtsFontGeneratorGDI; | |||||
| ftsCreator: TtsFontCreatorGDI; | |||||
| ftsFont: TtsFont; | ftsFont: TtsFont; | ||||
| procedure Render; | procedure Render; | ||||
| public | public | ||||
| @@ -47,9 +47,9 @@ begin | |||||
| fContext.BuildContext; | fContext.BuildContext; | ||||
| ftsContext := TtsContext.Create; | 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; | end; | ||||
| procedure TMainForm.FormDestroy(Sender: TObject); | procedure TMainForm.FormDestroy(Sender: TObject); | ||||
| @@ -86,9 +86,9 @@ begin | |||||
| glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | 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 | try | ||||
| block.HorzAlign := tsHorzAlignJustify; | |||||
| block.HorzAlign := TtsHorzAlignment.tsHorzAlignJustify; | |||||
| block.ChangeFont(ftsFont); | block.ChangeFont(ftsFont); | ||||
| block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | block.ChangeColor(tsColor4f(1.0, 1.0, 1.0, 1.0)); | ||||
| block.TextOutW(TEST_TEXT); | 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; | unit utsCodePages; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, SysUtils, utsTypes; | |||||
| type | |||||
| PtsCodePageValues = ^TtsCodePageValues; | |||||
| TtsCodePageValues = array [AnsiChar] of word; | |||||
| utsTypes; | |||||
| const | const | ||||
| CP_8859_2 : TtsCodePageValues = ( | 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 | $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 | implementation | ||||
| end. | 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; | unit utsFontCreatorFreeType; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, SysUtils, | Classes, SysUtils, | ||||
| utsTextSuite, utsTypes, utsFreeType; | |||||
| utsFreeType, utsFontCreator, utsFont, utsTypes, utsImage, utsContext; | |||||
| type | type | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| @@ -24,31 +24,25 @@ type | |||||
| TtsFontFreeType = class(TtsFont) | TtsFontFreeType = class(TtsFont) | ||||
| private | private | ||||
| fHandle: TtsFreeTypeFaceHandle; | fHandle: TtsFreeTypeFaceHandle; | ||||
| protected | |||||
| {%H-}constructor Create(const aHandle: TtsFreeTypeFaceHandle; const aCreator: TtsFontCreator; const aMetric: TtsFontMetric); | |||||
| public | 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; | destructor Destroy; override; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| TtsFontGeneratorFreeType = class(TtsFontGenerator) | |||||
| TtsFontCreatorFreeType = class(TtsFontCreator) | |||||
| private | private | ||||
| fHandle: FT_Library; | 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 | 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); | constructor Create(const aContext: TtsContext); | ||||
| destructor Destroy; override; | destructor Destroy; override; | ||||
| @@ -57,7 +51,8 @@ type | |||||
| implementation | implementation | ||||
| uses | uses | ||||
| utsUtils, math; | |||||
| Math, | |||||
| utsUtils; | |||||
| const | const | ||||
| FT_SIZE_FACTOR = 64; | FT_SIZE_FACTOR = 64; | ||||
| @@ -82,32 +77,150 @@ end; | |||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| //TtsFontFreeType/////////////////////////////////////////////////////////////////////////////////////////////////////// | //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 | begin | ||||
| inherited Create(aRenderer, aGenerator, aProperties); | |||||
| inherited Create(aCreator, aMetric); | |||||
| fHandle := aHandle; | fHandle := aHandle; | ||||
| end; | 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 | 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; | 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 | 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; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsFontGeneratorFreeType.LoadNames(const aFace: FT_Face; var aProperties: TtsFontProperties); | |||||
| //TtsFontCreatorFreeType////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| procedure TtsFontCreatorFreeType.LoadNames(const aFace: FT_Face; var aMetric: TtsFontMetric); | |||||
| var | var | ||||
| i, cnt: FT_Int; | i, cnt: FT_Int; | ||||
| err: FT_Error; | err: FT_Error; | ||||
| @@ -162,26 +275,26 @@ begin | |||||
| case name.name_id of | case name.name_id of | ||||
| TT_NAME_ID_COPYRIGHT: | TT_NAME_ID_COPYRIGHT: | ||||
| if (aProperties.Copyright = '') then | |||||
| aProperties.Copyright := Decode; | |||||
| if (aMetric.Copyright = '') then | |||||
| aMetric.Copyright := Decode; | |||||
| TT_NAME_ID_FONT_FAMILY: | TT_NAME_ID_FONT_FAMILY: | ||||
| if (aProperties.Fontname = '') then | |||||
| aProperties.Fontname := Decode; | |||||
| if (aMetric.Fontname = '') then | |||||
| aMetric.Fontname := Decode; | |||||
| TT_NAME_ID_FULL_NAME: | TT_NAME_ID_FULL_NAME: | ||||
| if (aProperties.FullName = '') then | |||||
| aProperties.FullName := Decode; | |||||
| if (aMetric.FullName = '') then | |||||
| aMetric.FullName := Decode; | |||||
| end; | end; | ||||
| end; | 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 | var | ||||
| err: FT_Error; | err: FT_Error; | ||||
| prop: TtsFontProperties; | |||||
| metric: TtsFontMetric; | |||||
| os2: PTT_OS2; | os2: PTT_OS2; | ||||
| hz: PTT_HoriHeader; | hz: PTT_HoriHeader; | ||||
| begin | begin | ||||
| @@ -189,173 +302,45 @@ begin | |||||
| if (err <> 0) then | if (err <> 0) then | ||||
| raise EtsException.Create('unable to set char size: error=' + IntToStr(err)); | 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 | 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 | 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)); | os2 := PTT_OS2(FT_Get_Sfnt_Table(aFace, FT_SFNT_OS2)); | ||||
| if Assigned(os2) and (os2^.version <> $FFFF) then begin | 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; | end; | ||||
| hz := PTT_HoriHeader(FT_Get_Sfnt_Table(aFace, FT_SFNT_HHEA)); | hz := PTT_HoriHeader(FT_Get_Sfnt_Table(aFace, FT_SFNT_HHEA)); | ||||
| if Assigned(hz) then begin | 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; | ||||
| 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; | 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 | var | ||||
| face: FT_Face; | face: FT_Face; | ||||
| err: FT_Error; | err: FT_Error; | ||||
| @@ -363,20 +348,22 @@ begin | |||||
| err := FT_New_Face(fHandle, PAnsiChar(aFilename), 0, @face); | err := FT_New_Face(fHandle, PAnsiChar(aFilename), 0, @face); | ||||
| if (err <> 0) then | if (err <> 0) then | ||||
| raise EtsException.Create('unable to create free type face from file: ' + aFilename + ' error=' + IntToStr(err)); | 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; | 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 | var | ||||
| face: FT_Face; | face: FT_Face; | ||||
| err: FT_Error; | err: FT_Error; | ||||
| ms: TMemoryStream; | ms: TMemoryStream; | ||||
| p: PBYte; | |||||
| begin | begin | ||||
| if (aStream is TMemoryStream) then begin | if (aStream is TMemoryStream) then begin | ||||
| ms := (aStream as TMemoryStream); | 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 | end else begin | ||||
| ms := TMemoryStream.Create; | ms := TMemoryStream.Create; | ||||
| try | try | ||||
| @@ -388,18 +375,18 @@ begin | |||||
| end; | end; | ||||
| if (err <> 0) then | if (err <> 0) then | ||||
| raise EtsException.Create('unable to create free type face from stream: error=' + IntToStr(err)); | 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; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| constructor TtsFontGeneratorFreeType.Create(const aContext: TtsContext); | |||||
| constructor TtsFontCreatorFreeType.Create(const aContext: TtsContext); | |||||
| begin | begin | ||||
| inherited Create(aContext); | inherited Create(aContext); | ||||
| fHandle := InitFreeType; | fHandle := InitFreeType; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| destructor TtsFontGeneratorFreeType.Destroy; | |||||
| destructor TtsFontCreatorFreeType.Destroy; | |||||
| begin | begin | ||||
| inherited Destroy; // first call interited | inherited Destroy; // first call interited | ||||
| QuitFreeType; // QuitFreeType will free callpacks | QuitFreeType; // QuitFreeType will free callpacks | ||||
| @@ -1,13 +1,14 @@ | |||||
| unit utsFreeType; | unit utsFreeType; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode delphi}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, SysUtils, syncobjs, dynlibs, utsTextSuite; | |||||
| Classes, SysUtils, syncobjs, {$IFDEF FPC}dynlibs,{$ELSE}windows,{$ENDIF} | |||||
| utsUtils; | |||||
| type | type | ||||
| // Simple Types | // Simple Types | ||||
| @@ -598,6 +599,16 @@ procedure QuitFreeType; | |||||
| implementation | implementation | ||||
| {$IFNDEF FPC} | |||||
| type | |||||
| TLibHandle = HMODULE; | |||||
| function GetLastOSError: Cardinal; | |||||
| begin | |||||
| result := GetLastError; | |||||
| end; | |||||
| {$ENDIF} | |||||
| {$IFDEF WINDOWS} | {$IFDEF WINDOWS} | ||||
| {$IFDEF WIN32} | {$IFDEF WIN32} | ||||
| {$DEFINE TS_FT_WIN32} | {$DEFINE TS_FT_WIN32} | ||||
| @@ -631,7 +642,7 @@ function InitFreeType: FT_Library; | |||||
| function GetProcAddr(const aName: String): Pointer; | function GetProcAddr(const aName: String): Pointer; | ||||
| begin | begin | ||||
| result := GetProcAddress(FreeTypeLibHandle, aName); | |||||
| result := GetProcAddress(FreeTypeLibHandle, PAnsiChar(aName)); | |||||
| if not Assigned(result) then | if not Assigned(result) then | ||||
| raise EtsException.Create('unable to load procedure from library: ' + aName); | raise EtsException.Create('unable to load procedure from library: ' + aName); | ||||
| end; | end; | ||||
| @@ -225,7 +225,7 @@ procedure QuitGDI; | |||||
| implementation | implementation | ||||
| uses | uses | ||||
| utsTextSuite; | |||||
| utsUtils; | |||||
| const | const | ||||
| LIB_GDI32 = 'gdi32.dll'; | 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; | unit utsOpenGLUtils; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, SysUtils, | Classes, SysUtils, | ||||
| utsTextSuite, utsTypes; | |||||
| utsTypes, utsRenderer, utsImage, utsContext, utsUtils, utsChar; | |||||
| type | type | ||||
| TtsCharRenderRefOpenGL = class(TtsCharRenderRef) | |||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| TtsOpenGLRenderRef = class(TObject) | |||||
| public | public | ||||
| TextureID: Integer; // ID of OpenGL texture where the char is stored in | |||||
| TextureID: Integer; | |||||
| Size: TtsPosition; | Size: TtsPosition; | ||||
| TexMat: TtsMatrix4f; | TexMat: TtsMatrix4f; | ||||
| VertMat: TtsMatrix4f; | VertMat: TtsMatrix4f; | ||||
| constructor Create; | constructor Create; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| PtsTextureUsageItem = ^TtsTextureUsageItem; | PtsTextureUsageItem = ^TtsTextureUsageItem; | ||||
| TtsTextureUsageItem = packed record | TtsTextureUsageItem = packed record | ||||
| children: array[0..3] of PtsTextureUsageItem; | children: array[0..3] of PtsTextureUsageItem; | ||||
| @@ -29,9 +31,10 @@ type | |||||
| TtsTextureTreeItem = packed record | TtsTextureTreeItem = packed record | ||||
| value: SmallInt; | value: SmallInt; | ||||
| children: array[0..1] of PtsTextureTreeItem; | children: array[0..1] of PtsTextureTreeItem; | ||||
| ref: TtsCharRenderRefOpenGL; | |||||
| ref: TtsOpenGLRenderRef; | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| PtsFontTexture = ^TtsFontTexture; | PtsFontTexture = ^TtsFontTexture; | ||||
| TtsFontTexture = packed record | TtsFontTexture = packed record | ||||
| ID: Integer; // OpenGL texture ID | ID: Integer; // OpenGL texture ID | ||||
| @@ -42,6 +45,7 @@ type | |||||
| Count: Integer; // number of chars stored in this texture | Count: Integer; // number of chars stored in this texture | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| TtsBaseOpenGL = class(TtsRenderer) | TtsBaseOpenGL = class(TtsRenderer) | ||||
| private | private | ||||
| fTextureSize: Integer; | fTextureSize: Integer; | ||||
| @@ -61,17 +65,16 @@ type | |||||
| function CreateNewTexture: PtsFontTexture; virtual; | function CreateNewTexture: PtsFontTexture; virtual; | ||||
| procedure FreeTexture(var aTexture: 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 | protected | ||||
| function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; override; | |||||
| procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); override; | |||||
| procedure BeginRender; 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 | public | ||||
| property TextureSize: Integer read fTextureSize write fTextureSize; | property TextureSize: Integer read fTextureSize write fTextureSize; | ||||
| @@ -84,9 +87,9 @@ type | |||||
| implementation | implementation | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| //TtsCharRenderRefOpenGL//////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //TtsOpenGLRenderRef//////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| constructor TtsCharRenderRefOpenGL.Create; | |||||
| constructor TtsOpenGLRenderRef.Create; | |||||
| begin | begin | ||||
| inherited Create; | inherited Create; | ||||
| TextureID := 0; | TextureID := 0; | ||||
| @@ -149,13 +152,14 @@ begin | |||||
| end; | 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 | begin | ||||
| // DUMMY | // DUMMY | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; | |||||
| function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef; | |||||
| var | var | ||||
| GlyphWidth, GlyphHeight: Integer; | GlyphWidth, GlyphHeight: Integer; | ||||
| @@ -199,7 +203,7 @@ var | |||||
| end; | end; | ||||
| end; | end; | ||||
| function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL; | |||||
| function AddToTexture(const aTexture: PtsFontTexture): TtsOpenGLRenderRef; | |||||
| var | var | ||||
| x, y, wChar, hChar, l, t: Integer; | x, y, wChar, hChar, l, t: Integer; | ||||
| item: PtsTextureTreeItem; | item: PtsTextureTreeItem; | ||||
| @@ -208,15 +212,15 @@ var | |||||
| if not Assigned(item) then | if not Assigned(item) then | ||||
| raise EtsRendererOpenGL.Create('unable to add glyph to texture'); | raise EtsRendererOpenGL.Create('unable to add glyph to texture'); | ||||
| item^.ref := TtsCharRenderRefOpenGL.Create; | |||||
| item^.ref := TtsOpenGLRenderRef.Create; | |||||
| result := item^.ref; | 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.TextureID := aTexture^.ID; | ||||
| result.Size := tsPosition(aCharImage.Width, aCharImage.Height); | |||||
| result.Size := tsPosition(aImage.Width, aImage.Height); | |||||
| result.TexMat := tsMatrix4f( | result.TexMat := tsMatrix4f( | ||||
| tsVector4f(wChar / aTexture^.Size, 0.0, 0.0, 0.0), | tsVector4f(wChar / aTexture^.Size, 0.0, 0.0, 0.0), | ||||
| tsVector4f(0.0, hChar / aTexture^.Size, 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(wChar, 0.0, 0.0, 0.0), | ||||
| tsVector4f(0.0, hChar, 0.0, 0.0), | tsVector4f(0.0, hChar, 0.0, 0.0), | ||||
| tsVector4f(0.0, 0.0, 1.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; | end; | ||||
| var | var | ||||
| tex: PtsFontTexture; | tex: PtsFontTexture; | ||||
| begin | begin | ||||
| result := nil; | result := nil; | ||||
| if aCharImage.IsEmpty then | |||||
| if aImage.IsEmpty then | |||||
| exit; | exit; | ||||
| GlyphWidth := aCharImage.Width + 1; | |||||
| GlyphHeight := aCharImage.Height + 1; | |||||
| GlyphWidth := aImage.Width + 1; | |||||
| GlyphHeight := aImage.Height + 1; | |||||
| // try to add to existing texture | // try to add to existing texture | ||||
| tex := fFirstTexture; | tex := fFirstTexture; | ||||
| @@ -250,7 +254,7 @@ begin | |||||
| // create new texture | // create new texture | ||||
| if not Assigned(result) then begin | 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) + ')'); | raise EtsRendererOpenGL.Create('char is to large to fit into a texture: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')'); | ||||
| tex := CreateNewTexture; | tex := CreateNewTexture; | ||||
| result := AddToTexture(tex); | result := AddToTexture(tex); | ||||
| @@ -261,9 +265,9 @@ begin | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsBaseOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef); | |||||
| procedure TtsBaseOpenGL.FreeRenderRef(const aRenderRef: TtsRenderRef); | |||||
| var | var | ||||
| ref: TtsCharRenderRefOpenGL; | |||||
| ref: TtsOpenGLRenderRef; | |||||
| tex: PtsFontTexture; | tex: PtsFontTexture; | ||||
| function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean; | function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean; | ||||
| @@ -310,10 +314,11 @@ var | |||||
| end; | end; | ||||
| begin | begin | ||||
| ref := nil; | |||||
| try | try | ||||
| if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then | |||||
| if not Assigned(aRenderRef) then | |||||
| exit; | exit; | ||||
| ref := (aCharRef as TtsCharRenderRefOpenGL); | |||||
| ref := TtsOpenGLRenderRef(aRenderRef); | |||||
| tex := fFirstTexture; | tex := fFirstTexture; | ||||
| while Assigned(tex) do begin | while Assigned(tex) do begin | ||||
| if (tex^.ID = ref.TextureID) then begin | if (tex^.ID = ref.TextureID) then begin | ||||
| @@ -329,24 +334,22 @@ begin | |||||
| tex := tex^.Next; | tex := tex^.Next; | ||||
| end; | end; | ||||
| finally | finally | ||||
| if Assigned(aCharRef) then | |||||
| aCharRef.Free; | |||||
| if Assigned(ref) then | |||||
| ref.Free; | |||||
| end; | end; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsBaseOpenGL.BeginRender; | procedure TtsBaseOpenGL.BeginRender; | ||||
| begin | begin | ||||
| inherited BeginRender; | |||||
| fRenderPos.x := 0; | fRenderPos.x := 0; | ||||
| fRenderPos.y := 0; | fRenderPos.y := 0; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsBaseOpenGL.SetDrawPos(const X, Y: Integer); | |||||
| procedure TtsBaseOpenGL.SetDrawPos(const aValue: TtsPosition); | |||||
| begin | begin | ||||
| fRenderPos.x := X; | |||||
| fRenderPos.y := Y; | |||||
| fRenderPos := aValue; | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| @@ -356,16 +359,16 @@ begin | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsBaseOpenGL.MoveDrawPos(const X, Y: Integer); | |||||
| procedure TtsBaseOpenGL.MoveDrawPos(const aOffset: TtsPosition); | |||||
| begin | begin | ||||
| fRenderPos.x := fRenderPos.x + X; | |||||
| fRenderPos.y := fRenderPos.y + Y; | |||||
| fRenderPos.x := fRenderPos.x + aOffset.x; | |||||
| fRenderPos.y := fRenderPos.y + aOffset.y; | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsBaseOpenGL.SetColor(const aColor: TtsColor4f); | |||||
| procedure TtsBaseOpenGL.SetColor(const aValue: TtsColor4f); | |||||
| begin | begin | ||||
| fColor := aColor; | |||||
| fColor := aValue; | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| @@ -379,6 +382,7 @@ begin | |||||
| fRenderPos := tsPosition(0, 0); | fRenderPos := tsPosition(0, 0); | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| destructor TtsBaseOpenGL.Destroy; | destructor TtsBaseOpenGL.Destroy; | ||||
| begin | begin | ||||
| FreeTextures(fFirstTexture); | 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; | unit utsRendererOpenGL; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, SysUtils, | |||||
| utsTextSuite, utsTypes, utsOpenGLUtils, dglOpenGL; | |||||
| Classes, SysUtils, dglOpenGL, | |||||
| utsOpenGLUtils, utsTypes, utsContext, utsImage; | |||||
| type | type | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| TtsRendererOpenGL = class(TtsBaseOpenGL) | TtsRendererOpenGL = class(TtsBaseOpenGL) | ||||
| private | private | ||||
| fVBO: GLuint; | fVBO: GLuint; | ||||
| fIsRendering: Boolean; | fIsRendering: Boolean; | ||||
| protected | protected | ||||
| function CreateNewTexture: PtsFontTexture; override; | |||||
| function CreateNewTexture: PtsFontTexture; override; | |||||
| procedure FreeTexture(var aTexture: 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 BeginRender; override; | ||||
| procedure EndRender; 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 | public | ||||
| constructor Create(const aContext: TtsContext; const aFormat: TtsFormat); | constructor Create(const aContext: TtsContext; const aFormat: TtsFormat); | ||||
| destructor Destroy; override; | destructor Destroy; override; | ||||
| @@ -35,6 +35,9 @@ type | |||||
| implementation | implementation | ||||
| uses | |||||
| utsUtils; | |||||
| type | type | ||||
| TVertex = packed record | TVertex = packed record | ||||
| pos: array[0..1] of GLfloat; | pos: array[0..1] of GLfloat; | ||||
| @@ -119,7 +122,8 @@ begin | |||||
| end; | 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 | begin | ||||
| glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID); | glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID); | ||||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | ||||
| @@ -156,40 +160,39 @@ begin | |||||
| glPopMatrix; | glPopMatrix; | ||||
| fIsRendering := false; | fIsRendering := false; | ||||
| end; | end; | ||||
| inherited EndRender; | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGL.SetDrawPos(const X, Y: Integer); | |||||
| procedure TtsRendererOpenGL.SetDrawPos(const aValue: TtsPosition); | |||||
| begin | begin | ||||
| inherited SetDrawPos(X, Y); | |||||
| inherited SetDrawPos(aValue); | |||||
| glPopMatrix; | glPopMatrix; | ||||
| glPushMatrix; | glPushMatrix; | ||||
| glTranslatef(X, Y, 0); | |||||
| glTranslatef(aValue.x, aValue.y, 0); | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGL.MoveDrawPos(const X, Y: Integer); | |||||
| procedure TtsRendererOpenGL.MoveDrawPos(const aOffset: TtsPosition); | |||||
| begin | begin | ||||
| inherited MoveDrawPos(X, Y); | |||||
| glTranslatef(X, Y, 0); | |||||
| inherited MoveDrawPos(aOffset); | |||||
| glTranslatef(aOffset.x, aOffset.y, 0); | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGL.SetColor(const aColor: TtsColor4f); | |||||
| procedure TtsRendererOpenGL.SetColor(const aValue: TtsColor4f); | |||||
| begin | begin | ||||
| inherited SetColor(aColor); | |||||
| inherited SetColor(aValue); | |||||
| glColor4fv(@Color.arr[0]); | glColor4fv(@Color.arr[0]); | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGL.Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer); | |||||
| procedure TtsRendererOpenGL.Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer); | |||||
| var | var | ||||
| ref: TtsCharRenderRefOpenGL; | |||||
| ref: TtsOpenGLRenderRef; | |||||
| m: TtsMatrix4f; | m: TtsMatrix4f; | ||||
| begin | 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); | glEnable(GL_TEXTURE_2D); | ||||
| glBindTexture(GL_TEXTURE_2D, ref.TextureID); | glBindTexture(GL_TEXTURE_2D, ref.TextureID); | ||||
| @@ -228,6 +231,7 @@ begin | |||||
| glBindBuffer(GL_ARRAY_BUFFER, 0); | glBindBuffer(GL_ARRAY_BUFFER, 0); | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| destructor TtsRendererOpenGL.Destroy; | destructor TtsRendererOpenGL.Destroy; | ||||
| begin | begin | ||||
| glDeleteBuffers(1, @fVBO); | glDeleteBuffers(1, @fVBO); | ||||
| @@ -1,15 +1,14 @@ | |||||
| unit utsRendererOpenGLES; | unit utsRendererOpenGLES; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| {.$DEFINE DEBUG} | |||||
| interface | interface | ||||
| uses | uses | ||||
| Classes, SysUtils, | |||||
| utsTextSuite, utsTypes, utsOpenGLUtils, dglOpenGLES; | |||||
| Classes, SysUtils, dglOpenGLES, | |||||
| utsOpenGLUtils, utsTypes, utsContext, utsImage; | |||||
| type | type | ||||
| TtsRendererOpenGLES = class(TtsBaseOpenGL) | TtsRendererOpenGLES = class(TtsBaseOpenGL) | ||||
| @@ -36,15 +35,14 @@ type | |||||
| protected | protected | ||||
| function CreateNewTexture: PtsFontTexture; override; | function CreateNewTexture: PtsFontTexture; override; | ||||
| procedure FreeTexture(var aTexture: 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 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 | public | ||||
| property ShaderProgram: GLuint read fShaderProgram write SetShaderProgram; | property ShaderProgram: GLuint read fShaderProgram write SetShaderProgram; | ||||
| property ProjectionMatrix: TtsMatrix4f read fProjMatrix write SetProjectionMatrix; | property ProjectionMatrix: TtsMatrix4f read fProjMatrix write SetProjectionMatrix; | ||||
| @@ -56,6 +54,9 @@ type | |||||
| implementation | implementation | ||||
| uses | |||||
| utsUtils, utsConstants; | |||||
| type | type | ||||
| TVertex = packed record | TVertex = packed record | ||||
| pos: array[0..1] of GLfloat; | pos: array[0..1] of GLfloat; | ||||
| @@ -316,7 +317,8 @@ begin | |||||
| end; | 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 | begin | ||||
| glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID); | glBindTexture(GL_TEXTURE_2D, aCharRef.TextureID); | ||||
| glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); | ||||
| @@ -335,34 +337,34 @@ begin | |||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGLES.SetDrawPos(const X, Y: Integer); | |||||
| procedure TtsRendererOpenGLES.SetDrawPos(const aValue: TtsPosition); | |||||
| begin | begin | ||||
| inherited SetDrawPos(X, Y); | |||||
| inherited SetDrawPos(aValue); | |||||
| UpdateUniformCharOffset; | UpdateUniformCharOffset; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGLES.MoveDrawPos(const X, Y: Integer); | |||||
| procedure TtsRendererOpenGLES.MoveDrawPos(const aOffset: TtsPosition); | |||||
| begin | begin | ||||
| inherited MoveDrawPos(X, Y); | |||||
| inherited MoveDrawPos(aOffset); | |||||
| UpdateUniformCharOffset; | UpdateUniformCharOffset; | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGLES.SetColor(const aColor: TtsColor4f); | |||||
| procedure TtsRendererOpenGLES.SetColor(const aValue: TtsColor4f); | |||||
| begin | begin | ||||
| inherited SetColor(aColor); | |||||
| inherited SetColor(aValue); | |||||
| glColor4f(Color.r, Color.g, Color.b, Color.a); | glColor4f(Color.r, Color.g, Color.b, Color.a); | ||||
| end; | end; | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| procedure TtsRendererOpenGLES.Render(const aCharRef: TtsCharRenderRef; const aForcedWidth: Integer); | |||||
| procedure TtsRendererOpenGLES.Render(const aRenderRef: TtsRenderRef; const aForcedWidth: Integer); | |||||
| var | var | ||||
| ref: TtsCharRenderRefOpenGL; | |||||
| ref: TtsOpenGLRenderRef; | |||||
| m: TtsMatrix4f; | m: TtsMatrix4f; | ||||
| begin | 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); | glBindTexture(GL_TEXTURE_2D, ref.TextureID); | ||||
| glBindBuffer(GL_ARRAY_BUFFER, fVBO); | glBindBuffer(GL_ARRAY_BUFFER, fVBO); | ||||
| @@ -424,3 +426,4 @@ begin | |||||
| end; | end; | ||||
| 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; | unit utsTypes; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | |||||
| Classes, SysUtils; | |||||
| type | type | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //Enumerations////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| {$Z4} | {$Z4} | ||||
| TtsCodePage = ( | TtsCodePage = ( | ||||
| tsUTF8, | tsUTF8, | ||||
| @@ -58,12 +58,12 @@ type | |||||
| tsISO_1257, | tsISO_1257, | ||||
| tsISO_1258); | tsISO_1258); | ||||
| TtsFontStyle = ( | |||||
| tsStyleBold, | |||||
| tsStyleItalic, | |||||
| tsStyleUnderline, | |||||
| tsStyleStrikeout); | |||||
| TtsFontStyles = set of TtsFontStyle; | |||||
| TtsFormat = ( | |||||
| tsFormatEmpty, | |||||
| tsFormatRGBA8, | |||||
| tsFormatLumAlpha8, | |||||
| tsFormatAlpha8, | |||||
| tsFormatLum8); | |||||
| TtsVertAlignment = ( | TtsVertAlignment = ( | ||||
| tsVertAlignTop, | tsVertAlignTop, | ||||
| @@ -76,17 +76,33 @@ type | |||||
| tsHorzAlignRight, | tsHorzAlignRight, | ||||
| tsHorzAlignJustify); | 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 = ( | TtsAntiAliasing = ( | ||||
| tsAANone, | tsAANone, | ||||
| tsAANormal); | tsAANormal); | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //Flags///////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||||
| TtsBlockFlag = ( | |||||
| tsBlockFlagWordWrap | |||||
| ); | |||||
| TtsBlockFlags = set of TtsBlockFlag; | |||||
| TtsFontStyle = ( | |||||
| tsStyleBold, | |||||
| tsStyleItalic, | |||||
| tsStyleUnderline, | |||||
| tsStyleStrikeout); | |||||
| TtsFontStyles = set of TtsFontStyle; | |||||
| TtsColorChannel = ( | TtsColorChannel = ( | ||||
| tsChannelRed, | tsChannelRed, | ||||
| tsChannelGreen, | tsChannelGreen, | ||||
| @@ -99,72 +115,42 @@ type | |||||
| tsModeReplace, | tsModeReplace, | ||||
| tsModeModulate); | tsModeModulate); | ||||
| TtsImageModes = array[TtsColorChannel] of TtsImageMode; | 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; | end; | ||||
| PtsPosition = ^TtsPosition; | |||||
| TtsPosition = packed record | TtsPosition = packed record | ||||
| x, y: Integer; | x, y: Integer; | ||||
| end; | 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; | 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; | end; | ||||
| PtsColor4ub = ^TtsColor4ub; | |||||
| TtsVector4f = array[0..3] of Single; | TtsVector4f = array[0..3] of Single; | ||||
| TtsMatrix4f = array[0..3] of TtsVector4f; | TtsMatrix4f = array[0..3] of TtsVector4f; | ||||
| TtsGlyphMetric = packed record | |||||
| GlyphOrigin: TtsPosition; | |||||
| GlyphRect: TtsRect; | |||||
| Advance: Integer; | |||||
| end; | |||||
| TtsTextMetric = packed record | TtsTextMetric = packed record | ||||
| Ascent: Integer; | Ascent: Integer; | ||||
| Descent: Integer; | Descent: Integer; | ||||
| @@ -175,239 +161,36 @@ type | |||||
| LineSpacing: Integer; | LineSpacing: Integer; | ||||
| end; | 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; | ||||
| 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. | end. | ||||
| @@ -1,30 +1,386 @@ | |||||
| unit utsUtils; | unit utsUtils; | ||||
| {$IFDEF FPC} | {$IFDEF FPC} | ||||
| {$mode delphi}{$H+} | |||||
| {$mode objfpc}{$H+} | |||||
| {$ENDIF} | {$ENDIF} | ||||
| interface | interface | ||||
| uses | 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 | implementation | ||||
| uses | 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; | function tsStrAlloc(aSize: Cardinal): PWideChar; | ||||
| @@ -205,7 +561,7 @@ var | |||||
| cp: PtsCodePageValues; | cp: PtsCodePageValues; | ||||
| begin | begin | ||||
| result := 0; | result := 0; | ||||
| cp := ANSI_TO_WIDE_CODE_PAGE_LUT[aCodePage]; | |||||
| cp := TS_CODE_PAGE_LUT[aCodePage]; | |||||
| if not Assigned(aDst) or | if not Assigned(aDst) or | ||||
| not Assigned(aSrc) or | not Assigned(aSrc) or | ||||
| not Assigned(cp) or | not Assigned(cp) or | ||||
| @@ -226,5 +582,164 @@ begin | |||||
| end; | end; | ||||
| 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. | end. | ||||