├── .gitattributes ├── .github └── workflows │ └── build-and-publish.yml ├── .gitignore ├── README.md ├── StbTrueTypeSharp.sln ├── generation ├── StbTrueTypeSharp.Generator.sln └── StbTrueTypeSharp.Generator │ ├── Program.cs │ ├── StbTrueTypeSharp.Generator.csproj │ └── stb_truetype.h ├── samples ├── OutputString │ ├── Fonts │ │ └── DroidSans.ttf │ ├── OutputString.csproj │ └── Program.cs └── StbTrueTypeSharp.MonoGame.Test │ ├── CharacterRange.cs │ ├── FontBaker.cs │ ├── FontBakerResult.cs │ ├── Fonts │ ├── DroidSans.ttf │ ├── DroidSansJapanese.ttf │ ├── KoPubBatang-Regular.ttf │ └── ZCOOLXiaoWei-Regular.ttf │ ├── Game1.cs │ ├── GlyphInfo.cs │ ├── Icon.ico │ ├── Program.cs │ └── StbTrueTypeSharp.MonoGame.Test.csproj ├── src ├── Hebron.Runtime │ ├── CRuntime.cs │ └── MemoryStats.cs ├── Properties │ └── AssemblyInfo.cs ├── StbTrueType.Generated.Bitmap.cs ├── StbTrueType.Generated.Buf.cs ├── StbTrueType.Generated.CharString.cs ├── StbTrueType.Generated.Common.cs ├── StbTrueType.Generated.FontInfo.cs ├── StbTrueType.Generated.Heap.cs ├── StbTrueType.Generated.RectPack.cs ├── StbTrueType.OldRasterizer.cs ├── StbTrueType.cs └── StbTrueTypeSharp.csproj └── tests ├── Resources └── DroidSans.ttf ├── StbTrueTypeSharp.Tests.csproj ├── Tests.cs └── Utility ├── Mathematics.cs └── Res.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-vendored -------------------------------------------------------------------------------- /.github/workflows/build-and-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build & Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | BuildAndPublish: 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | submodules: recursive 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: '6.0.x' 18 | - name: Build StbTrueTypeSharp 19 | run: dotnet build src\StbTrueTypeSharp.csproj --configuration Release 20 | - name: Test 21 | run: dotnet test StbTrueTypeSharp.sln 22 | - name: Install NuGet 23 | uses: NuGet/setup-nuget@v1 24 | - name: Publish StbTrueTypeSharp to NuGet 25 | run: nuget.exe push src\bin\Release\StbTrueTypeSharp.*.nupkg ${{secrets.NUGET_APIKEY}} -Source https://api.nuget.org/v3/index.json -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StbTrueTypeSharp 2 | [![NuGet](https://img.shields.io/nuget/v/StbTrueTypeSharp.svg)](https://www.nuget.org/packages/StbTrueTypeSharp/) ![Build & Publish](https://github.com/StbSharp/StbTrueTypeSharp/workflows/Build%20&%20Publish/badge.svg) [![Chat](https://img.shields.io/discord/628186029488340992.svg)](https://discord.gg/ZeHxhCY) 3 | 4 | C# port of stb_truetype.h 5 | 6 | # Adding Reference 7 | There are two ways of referencing StbTrueTypeSharp in the project: 8 | 1. Through nuget: `install-package StbTrueTypeSharp` 9 | 2. As submodule: 10 | 11 | a. `git submodule add https://github.com/StbSharp/StbTrueTypeSharp.git` 12 | 13 | b. Now there are two options: 14 | 15 | * Add StbTrueTypeSharp/src/StbTrueTypeSharp/StbTrueTypeSharp.csproj to the solution 16 | 17 | * Include *.cs from StbTrueTypeSharp/src/StbTrueTypeSharp directly in the project. In this case, it might make sense to add STBSHARP_INTERNAL build compilation symbol to the project, so StbTrueTypeSharp classes would become internal. 18 | 19 | # License 20 | Public Domain 21 | 22 | # Who uses it? 23 | https://github.com/rds1983/SpriteFontPlus 24 | 25 | https://github.com/rds1983/FontStashSharp 26 | 27 | ## Credits 28 | * [stb](https://github.com/nothings/stb) 29 | -------------------------------------------------------------------------------- /StbTrueTypeSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31410.357 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{14A355A8-A827-4D09-B3A4-4C7A0EF983EC}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StbSharp", "StbSharp", "{314ACA14-EC1F-4ECF-B902-A3D79850A1B2}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbTrueTypeSharp", "src\StbTrueTypeSharp.csproj", "{EC51229C-4DA6-4F17-AEFB-71166ED15036}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbTrueTypeSharp.MonoGame.Test", "samples\StbTrueTypeSharp.MonoGame.Test\StbTrueTypeSharp.MonoGame.Test.csproj", "{5528B473-7EEA-43EF-8DE2-6338C5F157D2}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbTrueTypeSharp.Tests", "tests\StbTrueTypeSharp.Tests.csproj", "{5856363B-38D5-4EF2-A383-1F469BD04AD0}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutputString", "samples\OutputString\OutputString.csproj", "{0EDFCA28-FDEB-4AF0-97E6-9F24D694DF97}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {EC51229C-4DA6-4F17-AEFB-71166ED15036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {EC51229C-4DA6-4F17-AEFB-71166ED15036}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {EC51229C-4DA6-4F17-AEFB-71166ED15036}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {EC51229C-4DA6-4F17-AEFB-71166ED15036}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {5528B473-7EEA-43EF-8DE2-6338C5F157D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {5528B473-7EEA-43EF-8DE2-6338C5F157D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {5528B473-7EEA-43EF-8DE2-6338C5F157D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {5528B473-7EEA-43EF-8DE2-6338C5F157D2}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {5856363B-38D5-4EF2-A383-1F469BD04AD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {5856363B-38D5-4EF2-A383-1F469BD04AD0}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {5856363B-38D5-4EF2-A383-1F469BD04AD0}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {5856363B-38D5-4EF2-A383-1F469BD04AD0}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {0EDFCA28-FDEB-4AF0-97E6-9F24D694DF97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {0EDFCA28-FDEB-4AF0-97E6-9F24D694DF97}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {0EDFCA28-FDEB-4AF0-97E6-9F24D694DF97}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {0EDFCA28-FDEB-4AF0-97E6-9F24D694DF97}.Release|Any CPU.Build.0 = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(NestedProjects) = preSolution 45 | {EC51229C-4DA6-4F17-AEFB-71166ED15036} = {314ACA14-EC1F-4ECF-B902-A3D79850A1B2} 46 | {5528B473-7EEA-43EF-8DE2-6338C5F157D2} = {14A355A8-A827-4D09-B3A4-4C7A0EF983EC} 47 | {0EDFCA28-FDEB-4AF0-97E6-9F24D694DF97} = {14A355A8-A827-4D09-B3A4-4C7A0EF983EC} 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {C9C812E7-F89A-4B6E-8F9D-981D5F8BBAB3} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /generation/StbTrueTypeSharp.Generator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29806.167 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbTrueTypeSharp.Generator", "StbTrueTypeSharp.Generator\StbTrueTypeSharp.Generator.csproj", "{002FAE39-051D-4BB2-B8D5-12543919E819}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hebron", "..\..\Hebron\src\Hebron\Hebron.csproj", "{E033D124-60E8-4BB8-9D01-B9DB7F716743}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {002FAE39-051D-4BB2-B8D5-12543919E819}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {002FAE39-051D-4BB2-B8D5-12543919E819}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {002FAE39-051D-4BB2-B8D5-12543919E819}.Release|Any CPU.ActiveCfg = Release|x64 19 | {E033D124-60E8-4BB8-9D01-B9DB7F716743}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {E033D124-60E8-4BB8-9D01-B9DB7F716743}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {E033D124-60E8-4BB8-9D01-B9DB7F716743}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {E033D124-60E8-4BB8-9D01-B9DB7F716743}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {ACF4E34C-5E6B-44ED-AA80-A1C43BA55B85} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /generation/StbTrueTypeSharp.Generator/Program.cs: -------------------------------------------------------------------------------- 1 | using Hebron; 2 | using Hebron.Roslyn; 3 | using Microsoft.CodeAnalysis; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | 10 | namespace StbSharp.StbTrueType.Generator 11 | { 12 | static class Program 13 | { 14 | private static readonly Dictionary _outputs = new Dictionary 15 | { 16 | ["Bitmap"] = new string[] 17 | { 18 | "stbtt__handle_clipped_edge", 19 | "stbtt__rasterize_sorted_edges", 20 | "stbtt__fill_active_edges_new", 21 | "stbtt__sort_edges_ins_sort", 22 | "stbtt__sort_edges_quicksort", 23 | "stbtt__sort_edges", 24 | "stbtt__bitmap", 25 | "stbtt__edge", 26 | "stbtt__active_edge", 27 | }, 28 | ["Buf"] = new string[] 29 | { 30 | "stbtt__buf", 31 | "stbtt__new_buf", 32 | 33 | }, 34 | ["CharString"] = new string[] 35 | { 36 | "stbtt__csctx" 37 | }, 38 | ["FontInfo"] = new string[] 39 | { 40 | "stbtt_fontinfo", 41 | "stbtt__close_shape", 42 | "stbtt__GetCoverageIndex", 43 | "stbtt__GetGlyphClass", 44 | "stbtt_kerningentry", 45 | }, 46 | ["RectPack"] = new string[] 47 | { 48 | "stbrp_context", 49 | "stbtt_pack_context", 50 | "stbtt_pack_range", 51 | "stbrp_node", 52 | "stbrp_rect", 53 | }, 54 | ["Heap"] = new string[] 55 | { 56 | "stbtt__hheap", 57 | "stbtt__hheap_chunk" 58 | } 59 | }; 60 | 61 | private static bool OwnedByClass(this string value, string cls) 62 | { 63 | return value.Contains("(" + cls + " ") || value.Contains("(" + cls + "*"); 64 | } 65 | 66 | private static void Write(Dictionary input, Dictionary output) where T : SyntaxNode 67 | { 68 | var keys = (from string k in input.Keys orderby k select k).ToArray(); 69 | foreach (var key in keys) 70 | { 71 | string outputKey = null; 72 | foreach (var pair2 in _outputs) 73 | { 74 | foreach (var prefix in pair2.Value) 75 | { 76 | if (key.StartsWith(prefix)) 77 | { 78 | outputKey = pair2.Key; 79 | goto found; 80 | } 81 | } 82 | } 83 | found: 84 | 85 | string value; 86 | using (var sw = new StringWriter()) 87 | { 88 | input[key].NormalizeWhitespace().WriteTo(sw); 89 | 90 | value = sw.ToString(); 91 | value += Environment.NewLine; 92 | } 93 | 94 | if (outputKey == null) 95 | { 96 | if (value.OwnedByClass("stbtt__bitmap")) 97 | { 98 | outputKey = "Bitmap"; 99 | } 100 | else if (value.OwnedByClass("stbtt__buf")) 101 | { 102 | outputKey = "Buf"; 103 | } 104 | else if (value.OwnedByClass("stbtt__csctx")) 105 | { 106 | outputKey = "CharString"; 107 | } 108 | else if (value.OwnedByClass("stbtt_fontinfo")) 109 | { 110 | outputKey = "FontInfo"; 111 | } 112 | else if (value.OwnedByClass("stbrp_context") || 113 | value.OwnedByClass("stbtt_pack_context")) 114 | { 115 | outputKey = "RectPack"; 116 | } 117 | else if (value.OwnedByClass("stbtt__hheap")) 118 | { 119 | outputKey = "Heap"; 120 | } 121 | } 122 | 123 | if (outputKey == null) 124 | { 125 | outputKey = "Common"; 126 | } 127 | 128 | if (!output.ContainsKey(outputKey)) 129 | { 130 | output[outputKey] = string.Empty; 131 | } 132 | 133 | output[outputKey] += value; 134 | } 135 | } 136 | 137 | private static string PostProcess(string data) 138 | { 139 | return data; 140 | } 141 | 142 | static void Process() 143 | { 144 | var parameters = new RoslynConversionParameters 145 | { 146 | InputPath = @"stb_truetype.h", 147 | Defines = new[] 148 | { 149 | "STB_TRUETYPE_IMPLEMENTATION", 150 | }, 151 | Classes = new[] 152 | { 153 | "stbtt_pack_context", 154 | "stbtt_fontinfo", 155 | }, 156 | SkipStructs = new string[] 157 | { 158 | "stbtt_fontinfo", 159 | }, 160 | SkipGlobalVariables = new string[] 161 | { 162 | }, 163 | SkipFunctions = new string[] 164 | { 165 | "stbtt__find_table", 166 | }, 167 | }; 168 | 169 | var dump = TextCodeConverter.Convert(parameters.InputPath, parameters.Defines); 170 | File.WriteAllText(@"..\..\..\..\..\..\src\StbTrueType.txt", dump); 171 | 172 | var result = RoslynCodeConverter.Convert(parameters); 173 | 174 | // Post processing 175 | Logger.Info("Post processing..."); 176 | 177 | var outputFiles = new Dictionary(); 178 | Write(result.NamedEnums, outputFiles); 179 | Write(result.UnnamedEnumValues, outputFiles); 180 | Write(result.GlobalVariables, outputFiles); 181 | Write(result.Delegates, outputFiles); 182 | Write(result.Structs, outputFiles); 183 | Write(result.Functions, outputFiles); 184 | 185 | foreach (var pair in outputFiles) 186 | { 187 | var data = PostProcess(pair.Value); 188 | 189 | var sb = new StringBuilder(); 190 | sb.AppendLine(string.Format("// Generated by Sichem at {0}", DateTime.Now)); 191 | sb.AppendLine(); 192 | 193 | sb.AppendLine("using System;"); 194 | sb.AppendLine("using System.Runtime.InteropServices;"); 195 | sb.AppendLine("using Hebron.Runtime;"); 196 | 197 | sb.AppendLine(); 198 | 199 | sb.Append("namespace StbTrueTypeSharp\n{\n\t"); 200 | sb.AppendLine("unsafe partial class StbTrueType\n\t{"); 201 | 202 | data = sb.ToString() + data; 203 | data += "}\n}"; 204 | 205 | var fileName = @"..\..\..\..\..\..\src\StbTrueType.Generated." + pair.Key + ".cs"; 206 | Logger.Info("Writing {0}", fileName); 207 | File.WriteAllText(fileName, data); 208 | } 209 | } 210 | 211 | static void Main(string[] args) 212 | { 213 | try 214 | { 215 | Process(); 216 | } 217 | catch (Exception ex) 218 | { 219 | Console.WriteLine(ex.Message); 220 | Console.WriteLine(ex.StackTrace); 221 | } 222 | 223 | Console.WriteLine("Finished. Press any key to quit."); 224 | Console.ReadKey(); 225 | } 226 | } 227 | } -------------------------------------------------------------------------------- /generation/StbTrueTypeSharp.Generator/StbTrueTypeSharp.Generator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | win-x64 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | PreserveNewest 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/OutputString/Fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/samples/OutputString/Fonts/DroidSans.ttf -------------------------------------------------------------------------------- /samples/OutputString/OutputString.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /samples/OutputString/Program.cs: -------------------------------------------------------------------------------- 1 | using StbImageWriteSharp; 2 | using System; 3 | using System.IO; 4 | using static StbTrueTypeSharp.StbTrueType; 5 | 6 | namespace OutputString 7 | { 8 | unsafe class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | if (args.Length < 1 || string.IsNullOrEmpty(args[0].Trim())) 13 | { 14 | Console.WriteLine("Usage: OutputString.exe "); 15 | return; 16 | } 17 | 18 | var s = args[0]; 19 | var fontSize = 32.0f; 20 | var bufferSize = 256; 21 | 22 | var bytes = File.ReadAllBytes("Fonts/DroidSans.ttf"); 23 | var buffer = new byte[bufferSize * bufferSize]; 24 | 25 | var info = new stbtt_fontinfo(); 26 | 27 | fixed (byte* ptr = bytes) 28 | { 29 | var res = stbtt_InitFont(info, ptr, 0); 30 | } 31 | 32 | int ascent, descent, lineGap; 33 | stbtt_GetFontVMetrics(info, &ascent, &descent, &lineGap); 34 | var lineHeight = ascent - descent + lineGap; 35 | 36 | var scale = stbtt_ScaleForPixelHeight(info, fontSize); 37 | 38 | ascent = (int)(ascent * scale + 0.5f); 39 | descent = (int)(descent * scale - 0.5f); 40 | lineHeight = (int)(lineHeight * scale + 0.5f); 41 | 42 | int posX = 0, posY = 0; 43 | 44 | for (var i = 0; i < s.Length; ++i) 45 | { 46 | var c = s[i]; 47 | var glyphId = stbtt_FindGlyphIndex(info, c); 48 | if (glyphId == 0) 49 | { 50 | continue; 51 | } 52 | 53 | int advanceTemp = 0, lsbTemp = 0; 54 | stbtt_GetGlyphHMetrics(info, glyphId, &advanceTemp, &lsbTemp); 55 | var advance = (int)(advanceTemp * scale + 0.5f); 56 | 57 | int x0, y0, x1, y1; 58 | stbtt_GetGlyphBitmapBox( 59 | info, glyphId, scale, scale, &x0, &y0, &x1, &y1 60 | ); 61 | 62 | posX += x0; 63 | posY = ascent + y0; 64 | 65 | if (posY < 0) 66 | { 67 | posY = 0; 68 | } 69 | 70 | fixed (byte* ptr = buffer) 71 | { 72 | var ptr2 = ptr + (posX + posY * bufferSize); 73 | 74 | stbtt_MakeGlyphBitmap( 75 | info, 76 | ptr2, 77 | x1 - x0, 78 | y1 - y0, 79 | bufferSize, 80 | scale, 81 | scale, 82 | glyphId 83 | ); 84 | } 85 | 86 | posX += advance; 87 | } 88 | 89 | var imageWriter = new ImageWriter(); 90 | using (var stream = File.OpenWrite("output.png")) 91 | { 92 | imageWriter.WritePng(buffer, bufferSize, bufferSize, ColorComponents.Grey, stream); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/CharacterRange.cs: -------------------------------------------------------------------------------- 1 | namespace StbSharp.MonoGame.Test 2 | { 3 | public struct CharacterRange 4 | { 5 | public static readonly CharacterRange BasicLatin = new CharacterRange(0x0020, 0x007F); 6 | public static readonly CharacterRange Latin1Supplement = new CharacterRange(0x00A0, 0x00FF); 7 | public static readonly CharacterRange LatinExtendedA = new CharacterRange(0x0100, 0x017F); 8 | public static readonly CharacterRange LatinExtendedB = new CharacterRange(0x0180, 0x024F); 9 | public static readonly CharacterRange Cyrillic = new CharacterRange(0x0400, 0x04FF); 10 | public static readonly CharacterRange CyrillicSupplement = new CharacterRange(0x0500, 0x052F); 11 | public static readonly CharacterRange Hiragana = new CharacterRange(0x3040, 0x309F); 12 | public static readonly CharacterRange Katakana = new CharacterRange(0x30A0, 0x30FF); 13 | public static readonly CharacterRange Greek = new CharacterRange(0x0370, 0x03FF); 14 | public static readonly CharacterRange CjkSymbolsAndPunctuation = new CharacterRange(0x3000, 0x303F); 15 | public static readonly CharacterRange CjkUnifiedIdeographs = new CharacterRange(0x4e00, 0x9fff); 16 | public static readonly CharacterRange HangulCompatibilityJamo = new CharacterRange(0x3130, 0x318f); 17 | public static readonly CharacterRange HangulSyllables = new CharacterRange(0xac00, 0xd7af); 18 | 19 | public int Start { get; } 20 | 21 | public int End { get; } 22 | 23 | public int Size => End - Start + 1; 24 | 25 | public CharacterRange(int start, int end) 26 | { 27 | Start = start; 28 | End = end; 29 | } 30 | 31 | public CharacterRange(int single) : this(single, single) 32 | { 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/FontBaker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using StbTrueTypeSharp; 5 | 6 | namespace StbSharp.MonoGame.Test 7 | { 8 | public unsafe class FontBaker 9 | { 10 | private byte[] _bitmap; 11 | private StbTrueType.stbtt_pack_context _context; 12 | private Dictionary _glyphs; 13 | private int bitmapWidth, bitmapHeight; 14 | 15 | public void Begin(int width, int height) 16 | { 17 | bitmapWidth = width; 18 | bitmapHeight = height; 19 | _bitmap = new byte[width * height]; 20 | _context = new StbTrueType.stbtt_pack_context(); 21 | 22 | fixed (byte* pixelsPtr = _bitmap) 23 | { 24 | StbTrueType.stbtt_PackBegin(_context, pixelsPtr, width, height, width, 1, null); 25 | } 26 | 27 | _glyphs = new Dictionary(); 28 | } 29 | 30 | public void Add(byte[] ttf, float fontPixelHeight, 31 | IEnumerable characterRanges) 32 | { 33 | if (ttf == null || ttf.Length == 0) 34 | throw new ArgumentNullException(nameof(ttf)); 35 | 36 | if (fontPixelHeight <= 0) 37 | throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); 38 | 39 | if (characterRanges == null) 40 | throw new ArgumentNullException(nameof(characterRanges)); 41 | 42 | if (!characterRanges.Any()) 43 | throw new ArgumentException("characterRanges must have a least one value."); 44 | 45 | var fontInfo = StbTrueType.CreateFont(ttf, 0); 46 | if (fontInfo == null) 47 | throw new Exception("Failed to init font."); 48 | 49 | var scaleFactor = StbTrueType.stbtt_ScaleForPixelHeight(fontInfo, fontPixelHeight); 50 | 51 | int ascent, descent, lineGap; 52 | StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); 53 | 54 | foreach (var range in characterRanges) 55 | { 56 | if (range.Start > range.End) 57 | continue; 58 | 59 | var cd = new StbTrueType.stbtt_packedchar[range.End - range.Start + 1]; 60 | fixed (StbTrueType.stbtt_packedchar* chardataPtr = cd) 61 | { 62 | StbTrueType.stbtt_PackFontRange(_context, fontInfo.data, 0, fontPixelHeight, 63 | range.Start, 64 | range.End - range.Start + 1, 65 | chardataPtr); 66 | } 67 | 68 | for (var i = 0; i < cd.Length; ++i) 69 | { 70 | var yOff = cd[i].yoff; 71 | yOff += ascent * scaleFactor; 72 | 73 | var glyphInfo = new GlyphInfo 74 | { 75 | X = cd[i].x0, 76 | Y = cd[i].y0, 77 | Width = cd[i].x1 - cd[i].x0, 78 | Height = cd[i].y1 - cd[i].y0, 79 | XOffset = (int)cd[i].xoff, 80 | YOffset = (int)Math.Round(yOff), 81 | XAdvance = (int)Math.Round(cd[i].xadvance) 82 | }; 83 | 84 | _glyphs[i + range.Start] = glyphInfo; 85 | } 86 | } 87 | } 88 | 89 | public FontBakerResult End() 90 | { 91 | return new FontBakerResult(_glyphs, _bitmap, bitmapWidth, bitmapHeight); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/FontBakerResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace StbSharp.MonoGame.Test 5 | { 6 | public class FontBakerResult 7 | { 8 | public FontBakerResult(Dictionary glyphs, byte[] bitmap, int width, int height) 9 | { 10 | if (glyphs == null) 11 | throw new ArgumentNullException(nameof(glyphs)); 12 | 13 | if (bitmap == null) 14 | throw new ArgumentNullException(nameof(bitmap)); 15 | 16 | if (width <= 0) 17 | throw new ArgumentOutOfRangeException(nameof(width)); 18 | 19 | if (height <= 0) 20 | throw new ArgumentOutOfRangeException(nameof(height)); 21 | 22 | if (bitmap.Length < width * height) 23 | throw new ArgumentException("pixels.Length should be higher than width * height"); 24 | 25 | Glyphs = glyphs; 26 | Bitmap = bitmap; 27 | Width = width; 28 | Height = height; 29 | } 30 | 31 | public Dictionary Glyphs { get; } 32 | 33 | public byte[] Bitmap { get; } 34 | 35 | public int Width { get; } 36 | 37 | public int Height { get; } 38 | } 39 | } -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/samples/StbTrueTypeSharp.MonoGame.Test/Fonts/DroidSans.ttf -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Fonts/DroidSansJapanese.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/samples/StbTrueTypeSharp.MonoGame.Test/Fonts/DroidSansJapanese.ttf -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Fonts/KoPubBatang-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/samples/StbTrueTypeSharp.MonoGame.Test/Fonts/KoPubBatang-Regular.ttf -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Fonts/ZCOOLXiaoWei-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/samples/StbTrueTypeSharp.MonoGame.Test/Fonts/ZCOOLXiaoWei-Regular.ttf -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Game1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Microsoft.Xna.Framework; 7 | using Microsoft.Xna.Framework.Graphics; 8 | using Microsoft.Xna.Framework.Input; 9 | 10 | namespace StbSharp.MonoGame.Test 11 | { 12 | /// 13 | /// This is the main type for your game. 14 | /// 15 | public class Game1 : Game 16 | { 17 | private const int FontBitmapWidth = 8192; 18 | private const int FontBitmapHeight = 8192; 19 | 20 | private SpriteFont _font; 21 | 22 | private GraphicsDeviceManager _graphics; 23 | private SpriteBatch _spriteBatch; 24 | 25 | public Game1() 26 | { 27 | _graphics = new GraphicsDeviceManager(this) 28 | { 29 | PreferredBackBufferWidth = 1920, 30 | PreferredBackBufferHeight = 1080, 31 | SynchronizeWithVerticalRetrace = false 32 | }; 33 | IsFixedTimeStep = false; 34 | 35 | Content.RootDirectory = "Content"; 36 | IsMouseVisible = true; 37 | Window.AllowUserResizing = true; 38 | } 39 | 40 | private void LoadFont() 41 | { 42 | var fontBaker = new FontBaker(); 43 | 44 | fontBaker.Begin(FontBitmapWidth, FontBitmapHeight); 45 | fontBaker.Add(File.ReadAllBytes("Fonts/DroidSans.ttf"), 32, new[] 46 | { 47 | CharacterRange.BasicLatin, 48 | CharacterRange.Latin1Supplement, 49 | CharacterRange.LatinExtendedA, 50 | CharacterRange.Cyrillic, 51 | CharacterRange.Greek 52 | }); 53 | 54 | fontBaker.Add(File.ReadAllBytes("Fonts/DroidSansJapanese.ttf"), 32, new[] 55 | { 56 | CharacterRange.Hiragana, 57 | CharacterRange.Katakana 58 | }); 59 | 60 | fontBaker.Add(File.ReadAllBytes("Fonts/ZCOOLXiaoWei-Regular.ttf"), 32, new[] 61 | { 62 | CharacterRange.CjkSymbolsAndPunctuation, 63 | CharacterRange.CjkUnifiedIdeographs 64 | }); 65 | 66 | fontBaker.Add(File.ReadAllBytes("Fonts/KoPubBatang-Regular.ttf"), 32, new[] 67 | { 68 | CharacterRange.HangulCompatibilityJamo, 69 | CharacterRange.HangulSyllables 70 | }); 71 | 72 | var _charData = fontBaker.End(); 73 | 74 | // Offset by minimal offset 75 | var minimumOffsetY = 10000; 76 | foreach (var pair in _charData.Glyphs) 77 | if (pair.Value.YOffset < minimumOffsetY) 78 | minimumOffsetY = pair.Value.YOffset; 79 | 80 | var keys = _charData.Glyphs.Keys.ToArray(); 81 | foreach (var key in keys) 82 | { 83 | var pc = _charData.Glyphs[key]; 84 | pc.YOffset -= minimumOffsetY; 85 | _charData.Glyphs[key] = pc; 86 | } 87 | 88 | var rgb = new Color[FontBitmapWidth * FontBitmapHeight]; 89 | for (var i = 0; i < _charData.Bitmap.Length; ++i) 90 | { 91 | var b = _charData.Bitmap[i]; 92 | rgb[i].R = b; 93 | rgb[i].G = b; 94 | rgb[i].B = b; 95 | 96 | rgb[i].A = b; 97 | } 98 | 99 | var fontTexture = new Texture2D(GraphicsDevice, FontBitmapWidth, FontBitmapHeight); 100 | fontTexture.SetData(rgb); 101 | 102 | var glyphBounds = new List(); 103 | var cropping = new List(); 104 | var chars = new List(); 105 | var kerning = new List(); 106 | 107 | var orderedKeys = _charData.Glyphs.Keys.OrderBy(a => a); 108 | foreach (var key in orderedKeys) 109 | { 110 | var character = _charData.Glyphs[key]; 111 | 112 | var bounds = new Rectangle(character.X, character.Y, 113 | character.Width, 114 | character.Height); 115 | 116 | glyphBounds.Add(bounds); 117 | cropping.Add(new Rectangle(character.XOffset, character.YOffset, bounds.Width, bounds.Height)); 118 | 119 | chars.Add((char)key); 120 | 121 | kerning.Add(new Vector3(0, bounds.Width, character.XAdvance - bounds.Width)); 122 | } 123 | 124 | var constructorInfo = typeof(SpriteFont).GetTypeInfo().DeclaredConstructors.First(); 125 | _font = (SpriteFont)constructorInfo.Invoke(new object[] 126 | { 127 | fontTexture, glyphBounds, cropping, 128 | chars, 20, 0, kerning, ' ' 129 | }); 130 | } 131 | 132 | /// 133 | /// LoadContent will be called once per game and is the place to load 134 | /// all of your content. 135 | /// 136 | protected override void LoadContent() 137 | { 138 | // Create a new SpriteBatch, which can be used to draw textures. 139 | _spriteBatch = new SpriteBatch(GraphicsDevice); 140 | 141 | // TODO: use this.Content to load your game content here 142 | // Create white texture 143 | 144 | // Load ttf 145 | LoadFont(); 146 | 147 | GC.Collect(); 148 | } 149 | 150 | /// 151 | /// UnloadContent will be called once per game and is the place to unload 152 | /// game-specific content. 153 | /// 154 | protected override void UnloadContent() 155 | { 156 | // TODO: Unload any non ContentManager content here 157 | } 158 | 159 | /// 160 | /// Allows the game to run logic such as updating the world, 161 | /// checking for collisions, gathering input, and playing audio. 162 | /// 163 | /// Provides a snapshot of timing values. 164 | protected override void Update(GameTime gameTime) 165 | { 166 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || 167 | Keyboard.GetState().IsKeyDown(Keys.Escape)) 168 | Exit(); 169 | 170 | base.Update(gameTime); 171 | } 172 | 173 | /// 174 | /// This is called when the game should draw itself. 175 | /// 176 | /// Provides a snapshot of timing values. 177 | protected override void Draw(GameTime gameTime) 178 | { 179 | GraphicsDevice.Clear(Color.CornflowerBlue); 180 | 181 | _spriteBatch.Begin(); 182 | 183 | // Draw alphabet for all common languages. 184 | _spriteBatch.DrawString(_font, 185 | "Eng: A a B b C c D d E e F f G g H h I i J j K k L l M m N n O o P p Q q R r S s T t U u V v W w X x Y y Z z", 186 | new Vector2(0, 0), Color.White); 187 | _spriteBatch.DrawString(_font, 188 | "Rus: А а, Б б, В в, Г г, Д д, Е е, Ё ё, Ж ж, З з, И и, Й й, К к, Л л, М м, Н н, О о, П п, Р р, С с, Т т, У у, Ф ф, Х х, Ц ц, Ч ч, Ш ш, Щ щ, Ъ ъ, Ы ы, Ь ь, Э э, Ю ю, Я я, І і, Ѳ ѳ, Ѣ ѣ, Ѵ ѵ", 189 | new Vector2(0, 30), Color.Maroon); 190 | _spriteBatch.DrawString(_font, 191 | "Scandinavian: Å å, Ø ø, Æ æ, œ, þ Fra: â ç è é ê î ô û ë ï ù á í ì ó ò ú, Romana: ă â î ș ț", 192 | new Vector2(0, 60), Color.Green); 193 | _spriteBatch.DrawString(_font, "Fra: â ç è é ê î ô û ë ï ù á í ì ó ò ú // Romana: ă â î ș ț", 194 | new Vector2(0, 90), Color.Navy); 195 | _spriteBatch.DrawString(_font, 196 | "Pol: Ą Ć Ę Ł Ń Ó Ś Ź Ż ą ć ę ł ń ó ś ź ż Zażółć gęślą jaźń, Prtgs: ã, õ, â, ê, ô, á, é, í, ó, ú, à, ç", 197 | new Vector2(0, 120), Color.Yellow); 198 | _spriteBatch.DrawString(_font, 199 | "Cze: ž š ů ě ř Příliš žluťoučký kůň úpěl ďábelské kódy, Lat/Lit: ā, č, ē, ģ, ī, ķ, ļ, ņ, ō, ŗ, š, ū, ž, ą, č, ę, ė, į, š, ų, ū, ž", 200 | new Vector2(0, 150), Color.Black); 201 | _spriteBatch.DrawString(_font, 202 | "Greek: Α α, Β β, Γ γ, Δ δ, Ε ε, Ζ ζ, Η η, Θ θ, Ι ι, Κ κ, Λ λ, Μ μ, Ν ν, Ξ ξ, Ο ο, Π π, Ρ ρ, Σ σ/ς, Τ τ, Υ υ, Φ φ, Χ χ, Ψ ψ, Ω ω ά έ ή ί ό ύ ώ", 203 | new Vector2(0, 180), Color.Aqua); 204 | _spriteBatch.DrawString(_font, "Jap: いろはにほ 。へどひらがなカタカナ, Kor: 한국어조선말, Cn: 国会这来对开关门时个书长万边东车爱儿。吾艾、贼德艾尺", 205 | new Vector2(0, 210), Color.Cyan); 206 | _spriteBatch.DrawString(_font, 207 | "Other symbols: Ñ ñ ¿ ¡ Ç ç á ê Ä ä à â Ö ö ô Ü ü ë ß ẞ Ÿ ÿ Œ Æ æ ï Ğ ğ Ş ş Ő ő Ű ű ù", 208 | new Vector2(0, 240), Color.Moccasin); 209 | 210 | // _spriteBatch.Draw(_font.Texture, new Vector2(0, 300)); 211 | 212 | _spriteBatch.End(); 213 | 214 | base.Draw(gameTime); 215 | } 216 | } 217 | } -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/GlyphInfo.cs: -------------------------------------------------------------------------------- 1 | namespace StbSharp.MonoGame.Test 2 | { 3 | public struct GlyphInfo 4 | { 5 | public int X, Y, Width, Height; 6 | public int XOffset, YOffset; 7 | public int XAdvance; 8 | } 9 | } -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/samples/StbTrueTypeSharp.MonoGame.Test/Icon.ico -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbSharp.MonoGame.Test 4 | { 5 | /// 6 | /// The main class. 7 | /// 8 | public static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | private static void Main() 15 | { 16 | using (var game = new Game1()) 17 | { 18 | game.Run(); 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /samples/StbTrueTypeSharp.MonoGame.Test/StbTrueTypeSharp.MonoGame.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | true 5 | WinExe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | PreserveNewest 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Hebron.Runtime/CRuntime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Hebron.Runtime 5 | { 6 | internal static unsafe class CRuntime 7 | { 8 | private static readonly string numbers = "0123456789"; 9 | 10 | public static void* malloc(ulong size) 11 | { 12 | return malloc((long)size); 13 | } 14 | 15 | public static void* malloc(long size) 16 | { 17 | var ptr = Marshal.AllocHGlobal((int)size); 18 | 19 | MemoryStats.Allocated(); 20 | 21 | return ptr.ToPointer(); 22 | } 23 | 24 | public static void free(void* a) 25 | { 26 | if (a == null) 27 | return; 28 | 29 | var ptr = new IntPtr(a); 30 | Marshal.FreeHGlobal(ptr); 31 | MemoryStats.Freed(); 32 | } 33 | 34 | public static void memcpy(void* a, void* b, long size) 35 | { 36 | var ap = (byte*)a; 37 | var bp = (byte*)b; 38 | for (long i = 0; i < size; ++i) 39 | *ap++ = *bp++; 40 | } 41 | 42 | public static void memcpy(void* a, void* b, ulong size) 43 | { 44 | memcpy(a, b, (long)size); 45 | } 46 | 47 | public static void memmove(void* a, void* b, long size) 48 | { 49 | void* temp = null; 50 | 51 | try 52 | { 53 | temp = malloc(size); 54 | memcpy(temp, b, size); 55 | memcpy(a, temp, size); 56 | } 57 | 58 | finally 59 | { 60 | if (temp != null) 61 | free(temp); 62 | } 63 | } 64 | 65 | public static void memmove(void* a, void* b, ulong size) 66 | { 67 | memmove(a, b, (long)size); 68 | } 69 | 70 | public static int memcmp(void* a, void* b, long size) 71 | { 72 | var result = 0; 73 | var ap = (byte*)a; 74 | var bp = (byte*)b; 75 | for (long i = 0; i < size; ++i) 76 | { 77 | if (*ap != *bp) 78 | result += 1; 79 | 80 | ap++; 81 | bp++; 82 | } 83 | 84 | return result; 85 | } 86 | 87 | public static int memcmp(void* a, void* b, ulong size) 88 | { 89 | return memcmp(a, b, (long)size); 90 | } 91 | 92 | public static int memcmp(byte* a, byte[] b, ulong size) 93 | { 94 | fixed (void* bptr = b) 95 | { 96 | return memcmp(a, bptr, (long)size); 97 | } 98 | } 99 | 100 | public static void memset(void* ptr, int value, long size) 101 | { 102 | var bptr = (byte*)ptr; 103 | var bval = (byte)value; 104 | for (long i = 0; i < size; ++i) 105 | *bptr++ = bval; 106 | } 107 | 108 | public static void memset(void* ptr, int value, ulong size) 109 | { 110 | memset(ptr, value, (long)size); 111 | } 112 | 113 | public static uint _lrotl(uint x, int y) 114 | { 115 | return (x << y) | (x >> (32 - y)); 116 | } 117 | 118 | public static void* realloc(void* a, long newSize) 119 | { 120 | if (a == null) 121 | return malloc(newSize); 122 | 123 | var ptr = new IntPtr(a); 124 | var result = Marshal.ReAllocHGlobal(ptr, new IntPtr(newSize)); 125 | 126 | return result.ToPointer(); 127 | } 128 | 129 | public static void* realloc(void* a, ulong newSize) 130 | { 131 | return realloc(a, (long)newSize); 132 | } 133 | 134 | public static int abs(int v) 135 | { 136 | return Math.Abs(v); 137 | } 138 | 139 | public static double pow(double a, double b) 140 | { 141 | return Math.Pow(a, b); 142 | } 143 | 144 | public static void SetArray(T[] data, T value) 145 | { 146 | for (var i = 0; i < data.Length; ++i) 147 | data[i] = value; 148 | } 149 | 150 | public static double ldexp(double number, int exponent) 151 | { 152 | return number * Math.Pow(2, exponent); 153 | } 154 | 155 | public static int strcmp(sbyte* src, string token) 156 | { 157 | var result = 0; 158 | 159 | for (var i = 0; i < token.Length; ++i) 160 | if (src[i] != token[i]) 161 | ++result; 162 | 163 | return result; 164 | } 165 | 166 | public static int strncmp(sbyte* src, string token, ulong size) 167 | { 168 | var result = 0; 169 | 170 | for (var i = 0; i < Math.Min(token.Length, (int)size); ++i) 171 | if (src[i] != token[i]) 172 | ++result; 173 | 174 | return result; 175 | } 176 | 177 | public static long strtol(sbyte* start, sbyte** end, int radix) 178 | { 179 | // First step - determine length 180 | var length = 0; 181 | var ptr = start; 182 | while (numbers.IndexOf((char)*ptr) != -1) 183 | { 184 | ++ptr; 185 | ++length; 186 | } 187 | 188 | long result = 0; 189 | 190 | // Now build up the number 191 | ptr = start; 192 | while (length > 0) 193 | { 194 | long num = numbers.IndexOf((char)*ptr); 195 | var pow = (long)Math.Pow(10, length - 1); 196 | result += num * pow; 197 | 198 | ++ptr; 199 | --length; 200 | } 201 | 202 | if (end != null) *end = ptr; 203 | 204 | return result; 205 | } 206 | 207 | public static float fabs(double a) 208 | { 209 | return (float)Math.Abs(a); 210 | } 211 | 212 | public static double ceil(double a) 213 | { 214 | return Math.Ceiling(a); 215 | } 216 | 217 | public static double floor(double a) 218 | { 219 | return Math.Floor(a); 220 | } 221 | 222 | public static double cos(double value) 223 | { 224 | return Math.Cos(value); 225 | } 226 | 227 | public static double acos(double value) 228 | { 229 | return Math.Acos(value); 230 | } 231 | 232 | public static double sin(double value) 233 | { 234 | return Math.Sin(value); 235 | } 236 | 237 | public static double sqrt(double val) 238 | { 239 | return Math.Sqrt(val); 240 | } 241 | 242 | public static double fmod(double x, double y) 243 | { 244 | return x % y; 245 | } 246 | 247 | public static ulong strlen(sbyte* str) 248 | { 249 | var ptr = str; 250 | 251 | while (*ptr != '\0') 252 | ptr++; 253 | 254 | return (ulong)ptr - (ulong)str - 1; 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /src/Hebron.Runtime/MemoryStats.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace Hebron.Runtime 4 | { 5 | internal static class MemoryStats 6 | { 7 | private static int _allocations; 8 | 9 | public static int Allocations => _allocations; 10 | 11 | internal static void Allocated() 12 | { 13 | Interlocked.Increment(ref _allocations); 14 | } 15 | 16 | internal static void Freed() 17 | { 18 | Interlocked.Decrement(ref _allocations); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("StbTrueTypeSharp.Tests")] 4 | -------------------------------------------------------------------------------- /src/StbTrueType.Generated.Bitmap.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using Hebron.Runtime; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace StbTrueTypeSharp 7 | { 8 | unsafe partial class StbTrueType 9 | { 10 | public static void stbtt__fill_active_edges_new(float* scanline, float* scanline_fill, int len, 11 | stbtt__active_edge* e, float y_top) 12 | { 13 | var y_bottom = y_top + 1; 14 | while (e != null) 15 | { 16 | if (e->fdx == 0) 17 | { 18 | var x0 = e->fx; 19 | if (x0 < len) 20 | { 21 | if (x0 >= 0) 22 | { 23 | stbtt__handle_clipped_edge(scanline, (int)x0, e, x0, y_top, x0, y_bottom); 24 | stbtt__handle_clipped_edge(scanline_fill - 1, (int)x0 + 1, e, x0, y_top, x0, y_bottom); 25 | } 26 | else 27 | { 28 | stbtt__handle_clipped_edge(scanline_fill - 1, 0, e, x0, y_top, x0, y_bottom); 29 | } 30 | } 31 | } 32 | else 33 | { 34 | var x0 = e->fx; 35 | var dx = e->fdx; 36 | var xb = x0 + dx; 37 | float x_top = 0; 38 | float x_bottom = 0; 39 | float sy0 = 0; 40 | float sy1 = 0; 41 | var dy = e->fdy; 42 | if (e->sy > y_top) 43 | { 44 | x_top = x0 + dx * (e->sy - y_top); 45 | sy0 = e->sy; 46 | } 47 | else 48 | { 49 | x_top = x0; 50 | sy0 = y_top; 51 | } 52 | 53 | if (e->ey < y_bottom) 54 | { 55 | x_bottom = x0 + dx * (e->ey - y_top); 56 | sy1 = e->ey; 57 | } 58 | else 59 | { 60 | x_bottom = xb; 61 | sy1 = y_bottom; 62 | } 63 | 64 | if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) 65 | { 66 | if ((int)x_top == (int)x_bottom) 67 | { 68 | float height = 0; 69 | var x = (int)x_top; 70 | height = (sy1 - sy0) * e->direction; 71 | scanline[x] += stbtt__position_trapezoid_area(height, x_top, x + 1.0f, x_bottom, x + 1.0f); 72 | scanline_fill[x] += height; 73 | } 74 | else 75 | { 76 | var x = 0; 77 | var x1 = 0; 78 | var x2 = 0; 79 | float y_crossing = 0; 80 | float y_final = 0; 81 | float step = 0; 82 | float sign = 0; 83 | float area = 0; 84 | if (x_top > x_bottom) 85 | { 86 | float t = 0; 87 | sy0 = y_bottom - (sy0 - y_top); 88 | sy1 = y_bottom - (sy1 - y_top); 89 | t = sy0; 90 | sy0 = sy1; 91 | sy1 = t; 92 | t = x_bottom; 93 | x_bottom = x_top; 94 | x_top = t; 95 | dx = -dx; 96 | dy = -dy; 97 | t = x0; 98 | x0 = xb; 99 | xb = t; 100 | } 101 | 102 | x1 = (int)x_top; 103 | x2 = (int)x_bottom; 104 | y_crossing = y_top + dy * (x1 + 1 - x0); 105 | y_final = y_top + dy * (x2 - x0); 106 | if (y_crossing > y_bottom) 107 | y_crossing = y_bottom; 108 | sign = e->direction; 109 | area = sign * (y_crossing - sy0); 110 | scanline[x1] += stbtt__sized_triangle_area(area, x1 + 1 - x_top); 111 | if (y_final > y_bottom) 112 | { 113 | y_final = y_bottom; 114 | dy = (y_final - y_crossing) / (x2 - (x1 + 1)); 115 | } 116 | 117 | step = sign * dy * 1; 118 | for (x = x1 + 1; x < x2; ++x) 119 | { 120 | scanline[x] += area + step / 2; 121 | area += step; 122 | } 123 | 124 | scanline[x2] += area + sign * 125 | stbtt__position_trapezoid_area(sy1 - y_final, x2, x2 + 1.0f, x_bottom, x2 + 1.0f); 126 | scanline_fill[x2] += sign * (sy1 - sy0); 127 | } 128 | } 129 | else 130 | { 131 | var x = 0; 132 | for (x = 0; x < len; ++x) 133 | { 134 | var y0 = y_top; 135 | float x1 = x; 136 | float x2 = x + 1; 137 | var x3 = xb; 138 | var y3 = y_bottom; 139 | var y1 = (x - x0) / dx + y_top; 140 | var y2 = (x + 1 - x0) / dx + y_top; 141 | if (x0 < x1 && x3 > x2) 142 | { 143 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1); 144 | stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x2, y2); 145 | stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3); 146 | } 147 | else if (x3 < x1 && x0 > x2) 148 | { 149 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2); 150 | stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x1, y1); 151 | stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3); 152 | } 153 | else if (x0 < x1 && x3 > x1) 154 | { 155 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1); 156 | stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3); 157 | } 158 | else if (x3 < x1 && x0 > x1) 159 | { 160 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x1, y1); 161 | stbtt__handle_clipped_edge(scanline, x, e, x1, y1, x3, y3); 162 | } 163 | else if (x0 < x2 && x3 > x2) 164 | { 165 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2); 166 | stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3); 167 | } 168 | else if (x3 < x2 && x0 > x2) 169 | { 170 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x2, y2); 171 | stbtt__handle_clipped_edge(scanline, x, e, x2, y2, x3, y3); 172 | } 173 | else 174 | { 175 | stbtt__handle_clipped_edge(scanline, x, e, x0, y0, x3, y3); 176 | } 177 | } 178 | } 179 | } 180 | 181 | e = e->next; 182 | } 183 | } 184 | 185 | public static void stbtt__handle_clipped_edge(float* scanline, int x, stbtt__active_edge* e, float x0, float y0, 186 | float x1, float y1) 187 | { 188 | if (y0 == y1) 189 | return; 190 | if (y0 > e->ey) 191 | return; 192 | if (y1 < e->sy) 193 | return; 194 | if (y0 < e->sy) 195 | { 196 | x0 += (x1 - x0) * (e->sy - y0) / (y1 - y0); 197 | y0 = e->sy; 198 | } 199 | 200 | if (y1 > e->ey) 201 | { 202 | x1 += (x1 - x0) * (e->ey - y1) / (y1 - y0); 203 | y1 = e->ey; 204 | } 205 | 206 | if (x0 <= x && x1 <= x) 207 | { 208 | scanline[x] += e->direction * (y1 - y0); 209 | } 210 | else if (x0 >= x + 1 && x1 >= x + 1) 211 | { 212 | } 213 | else 214 | { 215 | scanline[x] += e->direction * (y1 - y0) * (1 - (x0 - x + (x1 - x)) / 2); 216 | } 217 | } 218 | 219 | public static void stbtt__rasterize(stbtt__bitmap* result, stbtt__point* pts, int* wcount, int windings, 220 | float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, 221 | void* userdata, bool useOldRasterizer) 222 | { 223 | var y_scale_inv = invert != 0 ? -scale_y : scale_y; 224 | stbtt__edge* e; 225 | var n = 0; 226 | var i = 0; 227 | var j = 0; 228 | var k = 0; 229 | var m = 0; 230 | 231 | var vsubsample = 1; 232 | if (useOldRasterizer) 233 | { 234 | vsubsample = (result->h) < (8) ? 15 : 5; 235 | } 236 | 237 | n = 0; 238 | for (i = 0; i < windings; ++i) n += wcount[i]; 239 | 240 | e = (stbtt__edge*)CRuntime.malloc((ulong)(sizeof(stbtt__edge) * (n + 1))); 241 | if (e == null) 242 | return; 243 | n = 0; 244 | m = 0; 245 | for (i = 0; i < windings; ++i) 246 | { 247 | var p = pts + m; 248 | m += wcount[i]; 249 | j = wcount[i] - 1; 250 | for (k = 0; k < wcount[i]; j = k++) 251 | { 252 | var a = k; 253 | var b = j; 254 | if (p[j].y == p[k].y) 255 | continue; 256 | e[n].invert = 0; 257 | if (invert != 0 && p[j].y > p[k].y || 258 | invert == 0 && p[j].y < p[k].y) 259 | { 260 | e[n].invert = 1; 261 | a = j; 262 | b = k; 263 | } 264 | 265 | e[n].x0 = p[a].x * scale_x + shift_x; 266 | e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; 267 | e[n].x1 = p[b].x * scale_x + shift_x; 268 | e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; 269 | ++n; 270 | } 271 | } 272 | 273 | stbtt__sort_edges(e, n); 274 | 275 | if (!useOldRasterizer) 276 | { 277 | stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); 278 | } else 279 | { 280 | stbtt__rasterize_sorted_edges_old_rasterizer(result, e, n, vsubsample, off_x, off_y, userdata); 281 | } 282 | 283 | CRuntime.free(e); 284 | } 285 | 286 | public static void stbtt__rasterize_sorted_edges(stbtt__bitmap* result, stbtt__edge* e, int n, int vsubsample, 287 | int off_x, int off_y, void* userdata) 288 | { 289 | usedOldRasterizer = false; 290 | 291 | var hh = new stbtt__hheap(); 292 | stbtt__active_edge* active = null; 293 | var y = 0; 294 | var j = 0; 295 | var i = 0; 296 | var scanline_data = stackalloc float[129]; 297 | float* scanline; 298 | float* scanline2; 299 | if (result->w > 64) 300 | scanline = (float*)CRuntime.malloc((ulong)((result->w * 2 + 1) * sizeof(float))); 301 | else 302 | scanline = scanline_data; 303 | scanline2 = scanline + result->w; 304 | y = off_y; 305 | e[n].y0 = (float)(off_y + result->h) + 1; 306 | while (j < result->h) 307 | { 308 | var scan_y_top = y + 0.0f; 309 | var scan_y_bottom = y + 1.0f; 310 | var step = &active; 311 | CRuntime.memset(scanline, 0, (ulong)(result->w * sizeof(float))); 312 | CRuntime.memset(scanline2, 0, (ulong)((result->w + 1) * sizeof(float))); 313 | while (*step != null) 314 | { 315 | var z = *step; 316 | if (z->ey <= scan_y_top) 317 | { 318 | *step = z->next; 319 | z->direction = 0; 320 | stbtt__hheap_free(&hh, z); 321 | } 322 | else 323 | { 324 | step = &(*step)->next; 325 | } 326 | } 327 | 328 | while (e->y0 <= scan_y_bottom) 329 | { 330 | if (e->y0 != e->y1) 331 | { 332 | var z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); 333 | if (z != null) 334 | { 335 | if (j == 0 && off_y != 0) 336 | if (z->ey < scan_y_top) 337 | z->ey = scan_y_top; 338 | 339 | z->next = active; 340 | active = z; 341 | } 342 | } 343 | 344 | ++e; 345 | } 346 | 347 | if (active != null) 348 | stbtt__fill_active_edges_new(scanline, scanline2 + 1, result->w, active, scan_y_top); 349 | { 350 | float sum = 0; 351 | for (i = 0; i < result->w; ++i) 352 | { 353 | float k = 0; 354 | var m = 0; 355 | sum += scanline2[i]; 356 | k = scanline[i] + sum; 357 | k = CRuntime.fabs(k) * 255 + 0.5f; 358 | m = (int)k; 359 | if (m > 255) 360 | m = 255; 361 | result->pixels[j * result->stride + i] = (byte)m; 362 | } 363 | } 364 | 365 | step = &active; 366 | while (*step != null) 367 | { 368 | var z = *step; 369 | z->fx += z->fdx; 370 | step = &(*step)->next; 371 | } 372 | 373 | ++y; 374 | ++j; 375 | } 376 | 377 | stbtt__hheap_cleanup(&hh, userdata); 378 | if (scanline != scanline_data) 379 | CRuntime.free(scanline); 380 | } 381 | 382 | public static void stbtt__sort_edges(stbtt__edge* p, int n) 383 | { 384 | stbtt__sort_edges_quicksort(p, n); 385 | stbtt__sort_edges_ins_sort(p, n); 386 | } 387 | 388 | public static void stbtt__sort_edges_ins_sort(stbtt__edge* p, int n) 389 | { 390 | var i = 0; 391 | var j = 0; 392 | for (i = 1; i < n; ++i) 393 | { 394 | var t = p[i]; 395 | var a = &t; 396 | j = i; 397 | while (j > 0) 398 | { 399 | var b = &p[j - 1]; 400 | var c = a->y0 < b->y0 ? 1 : 0; 401 | if (c == 0) 402 | break; 403 | p[j] = p[j - 1]; 404 | --j; 405 | } 406 | 407 | if (i != j) 408 | p[j] = t; 409 | } 410 | } 411 | 412 | public static void stbtt__sort_edges_quicksort(stbtt__edge* p, int n) 413 | { 414 | while (n > 12) 415 | { 416 | var t = new stbtt__edge(); 417 | var c01 = 0; 418 | var c12 = 0; 419 | var c = 0; 420 | var m = 0; 421 | var i = 0; 422 | var j = 0; 423 | m = n >> 1; 424 | c01 = (&p[0])->y0 < (&p[m])->y0 ? 1 : 0; 425 | c12 = (&p[m])->y0 < (&p[n - 1])->y0 ? 1 : 0; 426 | if (c01 != c12) 427 | { 428 | var z = 0; 429 | c = (&p[0])->y0 < (&p[n - 1])->y0 ? 1 : 0; 430 | z = c == c12 ? 0 : n - 1; 431 | t = p[z]; 432 | p[z] = p[m]; 433 | p[m] = t; 434 | } 435 | 436 | t = p[0]; 437 | p[0] = p[m]; 438 | p[m] = t; 439 | i = 1; 440 | j = n - 1; 441 | for (; ; ) 442 | { 443 | for (; ; ++i) 444 | if (!((&p[i])->y0 < (&p[0])->y0)) 445 | break; 446 | 447 | for (; ; --j) 448 | if (!((&p[0])->y0 < (&p[j])->y0)) 449 | break; 450 | 451 | if (i >= j) 452 | break; 453 | t = p[i]; 454 | p[i] = p[j]; 455 | p[j] = t; 456 | ++i; 457 | --j; 458 | } 459 | 460 | if (j < n - i) 461 | { 462 | stbtt__sort_edges_quicksort(p, j); 463 | p = p + i; 464 | n = n - i; 465 | } 466 | else 467 | { 468 | stbtt__sort_edges_quicksort(p + i, n - i); 469 | n = j; 470 | } 471 | } 472 | } 473 | 474 | public static void stbtt_Rasterize(stbtt__bitmap* result, float flatness_in_pixels, stbtt_vertex* vertices, 475 | int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, 476 | void* userdata, bool useOldRasterizer) 477 | { 478 | var scale = scale_x > scale_y ? scale_y : scale_x; 479 | var winding_count = 0; 480 | int* winding_lengths = null; 481 | var windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, 482 | &winding_count, userdata); 483 | if (windings != null) 484 | { 485 | stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, 486 | x_off, y_off, (int)invert, userdata, useOldRasterizer); 487 | CRuntime.free(winding_lengths); 488 | CRuntime.free(windings); 489 | } 490 | } 491 | 492 | [StructLayout(LayoutKind.Sequential)] 493 | public struct stbtt__active_edge 494 | { 495 | public stbtt__active_edge* next; 496 | public float fx; 497 | public float fdx; 498 | public float fdy; 499 | public float direction; 500 | public float sy; 501 | public float ey; 502 | } 503 | 504 | [StructLayout(LayoutKind.Sequential)] 505 | public struct stbtt__bitmap 506 | { 507 | public int w; 508 | public int h; 509 | public int stride; 510 | public byte* pixels; 511 | } 512 | 513 | [StructLayout(LayoutKind.Sequential)] 514 | public struct stbtt__edge 515 | { 516 | public float x0; 517 | public float y0; 518 | public float x1; 519 | public float y1; 520 | public int invert; 521 | } 522 | } 523 | } -------------------------------------------------------------------------------- /src/StbTrueType.Generated.Buf.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using System.Runtime.InteropServices; 4 | 5 | namespace StbTrueTypeSharp 6 | { 7 | unsafe partial class StbTrueType 8 | { 9 | public static uint stbtt__buf_get(stbtt__buf* b, int n) 10 | { 11 | uint v = 0; 12 | var i = 0; 13 | for (i = 0; i < n; i++) v = (v << 8) | stbtt__buf_get8(b); 14 | 15 | return v; 16 | } 17 | 18 | public static byte stbtt__buf_get8(stbtt__buf* b) 19 | { 20 | if (b->cursor >= b->size) 21 | return 0; 22 | return b->data[b->cursor++]; 23 | } 24 | 25 | public static byte stbtt__buf_peek8(stbtt__buf* b) 26 | { 27 | if (b->cursor >= b->size) 28 | return 0; 29 | return b->data[b->cursor]; 30 | } 31 | 32 | public static stbtt__buf stbtt__buf_range(stbtt__buf* b, int o, int s) 33 | { 34 | var r = stbtt__new_buf(null, 0); 35 | if (o < 0 || s < 0 || o > b->size || s > b->size - o) 36 | return r; 37 | r.data = b->data + o; 38 | r.size = s; 39 | return r; 40 | } 41 | 42 | public static void stbtt__buf_seek(stbtt__buf* b, int o) 43 | { 44 | b->cursor = o > b->size || o < 0 ? b->size : o; 45 | } 46 | 47 | public static void stbtt__buf_skip(stbtt__buf* b, int o) 48 | { 49 | stbtt__buf_seek(b, b->cursor + o); 50 | } 51 | 52 | public static stbtt__buf stbtt__cff_get_index(stbtt__buf* b) 53 | { 54 | var count = 0; 55 | var start = 0; 56 | var offsize = 0; 57 | start = b->cursor; 58 | count = (int)stbtt__buf_get(b, 2); 59 | if (count != 0) 60 | { 61 | offsize = stbtt__buf_get8(b); 62 | stbtt__buf_skip(b, offsize * count); 63 | stbtt__buf_skip(b, (int)(stbtt__buf_get(b, offsize) - 1)); 64 | } 65 | 66 | return stbtt__buf_range(b, start, b->cursor - start); 67 | } 68 | 69 | public static int stbtt__cff_index_count(stbtt__buf* b) 70 | { 71 | stbtt__buf_seek(b, 0); 72 | return (int)stbtt__buf_get(b, 2); 73 | } 74 | 75 | public static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) 76 | { 77 | var count = 0; 78 | var offsize = 0; 79 | var start = 0; 80 | var end = 0; 81 | stbtt__buf_seek(&b, 0); 82 | count = (int)stbtt__buf_get(&b, 2); 83 | offsize = stbtt__buf_get8(&b); 84 | stbtt__buf_skip(&b, i * offsize); 85 | start = (int)stbtt__buf_get(&b, offsize); 86 | end = (int)stbtt__buf_get(&b, offsize); 87 | return stbtt__buf_range(&b, 2 + (count + 1) * offsize + start, end - start); 88 | } 89 | 90 | public static uint stbtt__cff_int(stbtt__buf* b) 91 | { 92 | int b0 = stbtt__buf_get8(b); 93 | if (b0 >= 32 && b0 <= 246) 94 | return (uint)(b0 - 139); 95 | if (b0 >= 247 && b0 <= 250) 96 | return (uint)((b0 - 247) * 256 + stbtt__buf_get8(b) + 108); 97 | if (b0 >= 251 && b0 <= 254) 98 | return (uint)(-(b0 - 251) * 256 - stbtt__buf_get8(b) - 108); 99 | if (b0 == 28) 100 | return stbtt__buf_get(b, 2); 101 | if (b0 == 29) 102 | return stbtt__buf_get(b, 4); 103 | return 0; 104 | } 105 | 106 | public static void stbtt__cff_skip_operand(stbtt__buf* b) 107 | { 108 | var v = 0; 109 | int b0 = stbtt__buf_peek8(b); 110 | if (b0 == 30) 111 | { 112 | stbtt__buf_skip(b, 1); 113 | while (b->cursor < b->size) 114 | { 115 | v = stbtt__buf_get8(b); 116 | if ((v & 0xF) == 0xF || v >> 4 == 0xF) 117 | break; 118 | } 119 | } 120 | else 121 | { 122 | stbtt__cff_int(b); 123 | } 124 | } 125 | 126 | public static stbtt__buf stbtt__dict_get(stbtt__buf* b, int key) 127 | { 128 | stbtt__buf_seek(b, 0); 129 | while (b->cursor < b->size) 130 | { 131 | var start = b->cursor; 132 | var end = 0; 133 | var op = 0; 134 | while (stbtt__buf_peek8(b) >= 28) stbtt__cff_skip_operand(b); 135 | 136 | end = b->cursor; 137 | op = stbtt__buf_get8(b); 138 | if (op == 12) 139 | op = stbtt__buf_get8(b) | 0x100; 140 | if (op == key) 141 | return stbtt__buf_range(b, start, end - start); 142 | } 143 | 144 | return stbtt__buf_range(b, 0, 0); 145 | } 146 | 147 | public static void stbtt__dict_get_ints(stbtt__buf* b, int key, int outcount, uint* _out_) 148 | { 149 | var i = 0; 150 | var operands = stbtt__dict_get(b, key); 151 | for (i = 0; i < outcount && operands.cursor < operands.size; i++) _out_[i] = stbtt__cff_int(&operands); 152 | } 153 | 154 | public static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) 155 | { 156 | var count = stbtt__cff_index_count(&idx); 157 | var bias = 107; 158 | if (count >= 33900) 159 | bias = 32768; 160 | else if (count >= 1240) 161 | bias = 1131; 162 | n += bias; 163 | if (n < 0 || n >= count) 164 | return stbtt__new_buf(null, 0); 165 | return stbtt__cff_index_get(idx, n); 166 | } 167 | 168 | public static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) 169 | { 170 | uint subrsoff = 0; 171 | var private_loc = stackalloc uint[] { 0, 0 }; 172 | var pdict = new stbtt__buf(); 173 | stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); 174 | if (private_loc[1] == 0 || private_loc[0] == 0) 175 | return stbtt__new_buf(null, 0); 176 | pdict = stbtt__buf_range(&cff, (int)private_loc[1], (int)private_loc[0]); 177 | stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); 178 | if (subrsoff == 0) 179 | return stbtt__new_buf(null, 0); 180 | stbtt__buf_seek(&cff, (int)(private_loc[1] + subrsoff)); 181 | return stbtt__cff_get_index(&cff); 182 | } 183 | 184 | public static stbtt__buf stbtt__new_buf(void* p, ulong size) 185 | { 186 | var r = new stbtt__buf(); 187 | r.data = (byte*)p; 188 | r.size = (int)size; 189 | r.cursor = 0; 190 | return r; 191 | } 192 | 193 | [StructLayout(LayoutKind.Sequential)] 194 | public struct stbtt__buf 195 | { 196 | public byte* data; 197 | public int cursor; 198 | public int size; 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /src/StbTrueType.Generated.CharString.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using System.Runtime.InteropServices; 4 | 5 | namespace StbTrueTypeSharp 6 | { 7 | unsafe partial class StbTrueType 8 | { 9 | public static void stbtt__csctx_close_shape(stbtt__csctx* ctx) 10 | { 11 | if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) 12 | stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); 13 | } 14 | 15 | public static void stbtt__csctx_rccurve_to(stbtt__csctx* ctx, float dx1, float dy1, float dx2, float dy2, 16 | float dx3, float dy3) 17 | { 18 | var cx1 = ctx->x + dx1; 19 | var cy1 = ctx->y + dy1; 20 | var cx2 = cx1 + dx2; 21 | var cy2 = cy1 + dy2; 22 | ctx->x = cx2 + dx3; 23 | ctx->y = cy2 + dy3; 24 | stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); 25 | } 26 | 27 | public static void stbtt__csctx_rline_to(stbtt__csctx* ctx, float dx, float dy) 28 | { 29 | ctx->x += dx; 30 | ctx->y += dy; 31 | stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); 32 | } 33 | 34 | public static void stbtt__csctx_rmove_to(stbtt__csctx* ctx, float dx, float dy) 35 | { 36 | stbtt__csctx_close_shape(ctx); 37 | ctx->first_x = ctx->x = ctx->x + dx; 38 | ctx->first_y = ctx->y = ctx->y + dy; 39 | stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); 40 | } 41 | 42 | public static void stbtt__csctx_v(stbtt__csctx* c, byte type, int x, int y, int cx, int cy, int cx1, int cy1) 43 | { 44 | if (c->bounds != 0) 45 | { 46 | stbtt__track_vertex(c, x, y); 47 | if (type == STBTT_vcubic) 48 | { 49 | stbtt__track_vertex(c, cx, cy); 50 | stbtt__track_vertex(c, cx1, cy1); 51 | } 52 | } 53 | else 54 | { 55 | stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); 56 | c->pvertices[c->num_vertices].cx1 = (short)cx1; 57 | c->pvertices[c->num_vertices].cy1 = (short)cy1; 58 | } 59 | 60 | c->num_vertices++; 61 | } 62 | 63 | public static void stbtt__track_vertex(stbtt__csctx* c, int x, int y) 64 | { 65 | if (x > c->max_x || c->started == 0) 66 | c->max_x = x; 67 | if (y > c->max_y || c->started == 0) 68 | c->max_y = y; 69 | if (x < c->min_x || c->started == 0) 70 | c->min_x = x; 71 | if (y < c->min_y || c->started == 0) 72 | c->min_y = y; 73 | c->started = 1; 74 | } 75 | 76 | [StructLayout(LayoutKind.Sequential)] 77 | public struct stbtt__csctx 78 | { 79 | public int bounds; 80 | public int started; 81 | public float first_x; 82 | public float first_y; 83 | public float x; 84 | public float y; 85 | public int min_x; 86 | public int max_x; 87 | public int min_y; 88 | public int max_y; 89 | public stbtt_vertex* pvertices; 90 | public int num_vertices; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/StbTrueType.Generated.Common.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using Hebron.Runtime; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace StbTrueTypeSharp 7 | { 8 | unsafe partial class StbTrueType 9 | { 10 | public const int STBTT_MAC_EID_ARABIC = 4; 11 | public const int STBTT_MAC_EID_CHINESE_TRAD = 2; 12 | public const int STBTT_MAC_EID_GREEK = 6; 13 | public const int STBTT_MAC_EID_HEBREW = 5; 14 | public const int STBTT_MAC_EID_JAPANESE = 1; 15 | public const int STBTT_MAC_EID_KOREAN = 3; 16 | public const int STBTT_MAC_EID_ROMAN = 0; 17 | public const int STBTT_MAC_EID_RUSSIAN = 7; 18 | public const int STBTT_MAC_LANG_ARABIC = 12; 19 | public const int STBTT_MAC_LANG_CHINESE_SIMPLIFIED = 33; 20 | public const int STBTT_MAC_LANG_CHINESE_TRAD = 19; 21 | public const int STBTT_MAC_LANG_DUTCH = 4; 22 | public const int STBTT_MAC_LANG_ENGLISH = 0; 23 | public const int STBTT_MAC_LANG_FRENCH = 1; 24 | public const int STBTT_MAC_LANG_GERMAN = 2; 25 | public const int STBTT_MAC_LANG_HEBREW = 10; 26 | public const int STBTT_MAC_LANG_ITALIAN = 3; 27 | public const int STBTT_MAC_LANG_JAPANESE = 11; 28 | public const int STBTT_MAC_LANG_KOREAN = 23; 29 | public const int STBTT_MAC_LANG_RUSSIAN = 32; 30 | public const int STBTT_MAC_LANG_SPANISH = 6; 31 | public const int STBTT_MAC_LANG_SWEDISH = 5; 32 | public const int STBTT_MS_EID_SHIFTJIS = 2; 33 | public const int STBTT_MS_EID_SYMBOL = 0; 34 | public const int STBTT_MS_EID_UNICODE_BMP = 1; 35 | public const int STBTT_MS_EID_UNICODE_FULL = 10; 36 | public const int STBTT_MS_LANG_CHINESE = 2052; 37 | public const int STBTT_MS_LANG_DUTCH = 1043; 38 | public const int STBTT_MS_LANG_ENGLISH = 1033; 39 | public const int STBTT_MS_LANG_FRENCH = 1036; 40 | public const int STBTT_MS_LANG_GERMAN = 1031; 41 | public const int STBTT_MS_LANG_HEBREW = 1037; 42 | public const int STBTT_MS_LANG_ITALIAN = 1040; 43 | public const int STBTT_MS_LANG_JAPANESE = 1041; 44 | public const int STBTT_MS_LANG_KOREAN = 1042; 45 | public const int STBTT_MS_LANG_RUSSIAN = 1049; 46 | public const int STBTT_MS_LANG_SPANISH = 1033; 47 | public const int STBTT_MS_LANG_SWEDISH = 1053; 48 | public const int STBTT_PLATFORM_ID_ISO = 2; 49 | public const int STBTT_PLATFORM_ID_MAC = 1; 50 | public const int STBTT_PLATFORM_ID_MICROSOFT = 3; 51 | public const int STBTT_PLATFORM_ID_UNICODE = 0; 52 | public const int STBTT_UNICODE_EID_ISO_10646 = 2; 53 | public const int STBTT_UNICODE_EID_UNICODE_1_0 = 0; 54 | public const int STBTT_UNICODE_EID_UNICODE_1_1 = 1; 55 | public const int STBTT_UNICODE_EID_UNICODE_2_0_BMP = 3; 56 | public const int STBTT_UNICODE_EID_UNICODE_2_0_FULL = 4; 57 | public const int STBTT_vcubic = 4; 58 | public const int STBTT_vcurve = 3; 59 | public const int STBTT_vline = 2; 60 | public const int STBTT_vmove = 1; 61 | 62 | public static int equal(float* a, float* b) 63 | { 64 | return a[0] == b[0] && a[1] == b[1] ? 1 : 0; 65 | } 66 | 67 | public static void stbtt__add_point(stbtt__point* points, int n, float x, float y) 68 | { 69 | if (points == null) 70 | return; 71 | points[n].x = x; 72 | points[n].y = y; 73 | } 74 | 75 | public static int stbtt__CompareUTF8toUTF16_bigendian_prefix(byte* s1, int len1, byte* s2, int len2) 76 | { 77 | var i = 0; 78 | while (len2 != 0) 79 | { 80 | var ch = (ushort)(s2[0] * 256 + s2[1]); 81 | if (ch < 0x80) 82 | { 83 | if (i >= len1) 84 | return -1; 85 | if (s1[i++] != ch) 86 | return -1; 87 | } 88 | else if (ch < 0x800) 89 | { 90 | if (i + 1 >= len1) 91 | return -1; 92 | if (s1[i++] != 0xc0 + (ch >> 6)) 93 | return -1; 94 | if (s1[i++] != 0x80 + (ch & 0x3f)) 95 | return -1; 96 | } 97 | else if (ch >= 0xd800 && ch < 0xdc00) 98 | { 99 | uint c = 0; 100 | var ch2 = (ushort)(s2[2] * 256 + s2[3]); 101 | if (i + 3 >= len1) 102 | return -1; 103 | c = (uint)(((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000); 104 | if (s1[i++] != 0xf0 + (c >> 18)) 105 | return -1; 106 | if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) 107 | return -1; 108 | if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) 109 | return -1; 110 | if (s1[i++] != 0x80 + (c & 0x3f)) 111 | return -1; 112 | s2 += 2; 113 | len2 -= 2; 114 | } 115 | else if (ch >= 0xdc00 && ch < 0xe000) 116 | { 117 | return -1; 118 | } 119 | else 120 | { 121 | if (i + 2 >= len1) 122 | return -1; 123 | if (s1[i++] != 0xe0 + (ch >> 12)) 124 | return -1; 125 | if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) 126 | return -1; 127 | if (s1[i++] != 0x80 + (ch & 0x3f)) 128 | return -1; 129 | } 130 | 131 | s2 += 2; 132 | len2 -= 2; 133 | } 134 | 135 | return i; 136 | } 137 | 138 | public static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex* verts) 139 | { 140 | var i = 0; 141 | var orig = stackalloc float[2]; 142 | var ray = stackalloc float[] { 1, 0 }; 143 | float y_frac = 0; 144 | var winding = 0; 145 | y_frac = (float)CRuntime.fmod(y, 1.0f); 146 | if (y_frac < 0.01f) 147 | y += 0.01f; 148 | else if (y_frac > 0.99f) 149 | y -= 0.01f; 150 | orig[0] = x; 151 | orig[1] = y; 152 | for (i = 0; i < nverts; ++i) 153 | { 154 | if (verts[i].type == STBTT_vline) 155 | { 156 | int x0 = verts[i - 1].x; 157 | int y0 = verts[i - 1].y; 158 | int x1 = verts[i].x; 159 | int y1 = verts[i].y; 160 | if (y > (y0 < y1 ? y0 : y1) && y < (y0 < y1 ? y1 : y0) && x > (x0 < x1 ? x0 : x1)) 161 | { 162 | var x_inter = (y - y0) / (y1 - y0) * (x1 - x0) + x0; 163 | if (x_inter < x) 164 | winding += y0 < y1 ? 1 : -1; 165 | } 166 | } 167 | 168 | if (verts[i].type == STBTT_vcurve) 169 | { 170 | int x0 = verts[i - 1].x; 171 | int y0 = verts[i - 1].y; 172 | int x1 = verts[i].cx; 173 | int y1 = verts[i].cy; 174 | int x2 = verts[i].x; 175 | int y2 = verts[i].y; 176 | var ax = x0 < (x1 < x2 ? x1 : x2) ? x0 : x1 < x2 ? x1 : x2; 177 | var ay = y0 < (y1 < y2 ? y1 : y2) ? y0 : y1 < y2 ? y1 : y2; 178 | var by = y0 < (y1 < y2 ? y2 : y1) ? y1 < y2 ? y2 : y1 : y0; 179 | if (y > ay && y < by && x > ax) 180 | { 181 | var q0 = stackalloc float[2]; 182 | var q1 = stackalloc float[2]; 183 | var q2 = stackalloc float[2]; 184 | var hits = stackalloc float[4]; 185 | q0[0] = x0; 186 | q0[1] = y0; 187 | q1[0] = x1; 188 | q1[1] = y1; 189 | q2[0] = x2; 190 | q2[1] = y2; 191 | if (equal(q0, q1) != 0 || equal(q1, q2) != 0) 192 | { 193 | x0 = verts[i - 1].x; 194 | y0 = verts[i - 1].y; 195 | x1 = verts[i].x; 196 | y1 = verts[i].y; 197 | if (y > (y0 < y1 ? y0 : y1) && y < (y0 < y1 ? y1 : y0) && x > (x0 < x1 ? x0 : x1)) 198 | { 199 | var x_inter = (y - y0) / (y1 - y0) * (x1 - x0) + x0; 200 | if (x_inter < x) 201 | winding += y0 < y1 ? 1 : -1; 202 | } 203 | } 204 | else 205 | { 206 | var num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); 207 | if (num_hits >= 1) 208 | if (hits[0] < 0) 209 | winding += hits[1] < 0 ? -1 : 1; 210 | if (num_hits >= 2) 211 | if (hits[2] < 0) 212 | winding += hits[3] < 0 ? -1 : 1; 213 | } 214 | } 215 | } 216 | } 217 | 218 | return winding; 219 | } 220 | 221 | public static float stbtt__cuberoot(float x) 222 | { 223 | if (x < 0) 224 | return -(float)CRuntime.pow(-x, 1.0f / 3.0f); 225 | return (float)CRuntime.pow(x, 1.0f / 3.0f); 226 | } 227 | 228 | public static void stbtt__h_prefilter(byte* pixels, int w, int h, int stride_in_bytes, uint kernel_width) 229 | { 230 | var buffer = stackalloc byte[8]; 231 | var safe_w = (int)(w - kernel_width); 232 | var j = 0; 233 | CRuntime.memset(buffer, 0, (ulong)8); 234 | for (j = 0; j < h; ++j) 235 | { 236 | var i = 0; 237 | uint total = 0; 238 | CRuntime.memset(buffer, 0, (ulong)kernel_width); 239 | total = 0; 240 | switch (kernel_width) 241 | { 242 | case 2: 243 | for (i = 0; i <= safe_w; ++i) 244 | { 245 | total += (uint)(pixels[i] - buffer[i & (8 - 1)]); 246 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i]; 247 | pixels[i] = (byte)(total / 2); 248 | } 249 | 250 | break; 251 | case 3: 252 | for (i = 0; i <= safe_w; ++i) 253 | { 254 | total += (uint)(pixels[i] - buffer[i & (8 - 1)]); 255 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i]; 256 | pixels[i] = (byte)(total / 3); 257 | } 258 | 259 | break; 260 | case 4: 261 | for (i = 0; i <= safe_w; ++i) 262 | { 263 | total += (uint)(pixels[i] - buffer[i & (8 - 1)]); 264 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i]; 265 | pixels[i] = (byte)(total / 4); 266 | } 267 | 268 | break; 269 | case 5: 270 | for (i = 0; i <= safe_w; ++i) 271 | { 272 | total += (uint)(pixels[i] - buffer[i & (8 - 1)]); 273 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i]; 274 | pixels[i] = (byte)(total / 5); 275 | } 276 | 277 | break; 278 | default: 279 | for (i = 0; i <= safe_w; ++i) 280 | { 281 | total += (uint)(pixels[i] - buffer[i & (8 - 1)]); 282 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i]; 283 | pixels[i] = (byte)(total / kernel_width); 284 | } 285 | 286 | break; 287 | } 288 | 289 | for (; i < w; ++i) 290 | { 291 | total -= buffer[i & (8 - 1)]; 292 | pixels[i] = (byte)(total / kernel_width); 293 | } 294 | 295 | pixels += stride_in_bytes; 296 | } 297 | } 298 | 299 | public static int stbtt__isfont(byte* font) 300 | { 301 | if (font[0] == 49 && font[1] == 0 && font[2] == 0 && font[3] == 0) 302 | return 1; 303 | if (font[0] == "typ1"[0] && font[1] == "typ1"[1] && font[2] == "typ1"[2] && font[3] == "typ1"[3]) 304 | return 1; 305 | if (font[0] == "OTTO"[0] && font[1] == "OTTO"[1] && font[2] == "OTTO"[2] && font[3] == "OTTO"[3]) 306 | return 1; 307 | if (font[0] == 0 && font[1] == 1 && font[2] == 0 && font[3] == 0) 308 | return 1; 309 | if (font[0] == "true"[0] && font[1] == "true"[1] && font[2] == "true"[2] && font[3] == "true"[3]) 310 | return 1; 311 | return 0; 312 | } 313 | 314 | public static int stbtt__matches(byte* fc, uint offset, byte* name, int flags) 315 | { 316 | var nlen = (int)CRuntime.strlen((sbyte*)name); 317 | uint nm = 0; 318 | uint hd = 0; 319 | if (stbtt__isfont(fc + offset) == 0) 320 | return 0; 321 | if (flags != 0) 322 | { 323 | hd = stbtt__find_table(fc, offset, "head"); 324 | if ((ttUSHORT(fc + hd + 44) & 7) != (flags & 7)) 325 | return 0; 326 | } 327 | 328 | nm = stbtt__find_table(fc, offset, "name"); 329 | if (nm == 0) 330 | return 0; 331 | if (flags != 0) 332 | { 333 | if (stbtt__matchpair(fc, nm, name, nlen, 16, -1) != 0) 334 | return 1; 335 | if (stbtt__matchpair(fc, nm, name, nlen, 1, -1) != 0) 336 | return 1; 337 | if (stbtt__matchpair(fc, nm, name, nlen, 3, -1) != 0) 338 | return 1; 339 | } 340 | else 341 | { 342 | if (stbtt__matchpair(fc, nm, name, nlen, 16, 17) != 0) 343 | return 1; 344 | if (stbtt__matchpair(fc, nm, name, nlen, 1, 2) != 0) 345 | return 1; 346 | if (stbtt__matchpair(fc, nm, name, nlen, 3, -1) != 0) 347 | return 1; 348 | } 349 | 350 | return 0; 351 | } 352 | 353 | public static int stbtt__matchpair(byte* fc, uint nm, byte* name, int nlen, int target_id, int next_id) 354 | { 355 | var i = 0; 356 | int count = ttUSHORT(fc + nm + 2); 357 | var stringOffset = (int)(nm + ttUSHORT(fc + nm + 4)); 358 | for (i = 0; i < count; ++i) 359 | { 360 | var loc = (uint)(nm + 6 + 12 * i); 361 | int id = ttUSHORT(fc + loc + 6); 362 | if (id == target_id) 363 | { 364 | int platform = ttUSHORT(fc + loc + 0); 365 | int encoding = ttUSHORT(fc + loc + 2); 366 | int language = ttUSHORT(fc + loc + 4); 367 | if (platform == 0 || platform == 3 && encoding == 1 || platform == 3 && encoding == 10) 368 | { 369 | int slen = ttUSHORT(fc + loc + 8); 370 | int off = ttUSHORT(fc + loc + 10); 371 | var matchlen = 372 | stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc + stringOffset + off, slen); 373 | if (matchlen >= 0) 374 | { 375 | if (i + 1 < count && ttUSHORT(fc + loc + 12 + 6) == next_id && 376 | ttUSHORT(fc + loc + 12) == platform && ttUSHORT(fc + loc + 12 + 2) == encoding && 377 | ttUSHORT(fc + loc + 12 + 4) == language) 378 | { 379 | slen = ttUSHORT(fc + loc + 12 + 8); 380 | off = ttUSHORT(fc + loc + 12 + 10); 381 | if (slen == 0) 382 | { 383 | if (matchlen == nlen) 384 | return 1; 385 | } 386 | else if (matchlen < nlen && name[matchlen] == 32) 387 | { 388 | ++matchlen; 389 | if (stbtt_CompareUTF8toUTF16_bigendian_internal((sbyte*)(name + matchlen), 390 | nlen - matchlen, (sbyte*)(fc + stringOffset + off), slen) != 0) 391 | return 1; 392 | } 393 | } 394 | else 395 | { 396 | if (matchlen == nlen) 397 | return 1; 398 | } 399 | } 400 | } 401 | } 402 | } 403 | 404 | return 0; 405 | } 406 | 407 | public static float stbtt__oversample_shift(int oversample) 408 | { 409 | if (oversample == 0) 410 | return 0.0f; 411 | return -(oversample - 1) / (2.0f * oversample); 412 | } 413 | 414 | public static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) 415 | { 416 | return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); 417 | } 418 | 419 | public static int stbtt__ray_intersect_bezier(float* orig, float* ray, float* q0, float* q1, float* q2, 420 | float* hits) 421 | { 422 | var q0perp = q0[1] * ray[0] - q0[0] * ray[1]; 423 | var q1perp = q1[1] * ray[0] - q1[0] * ray[1]; 424 | var q2perp = q2[1] * ray[0] - q2[0] * ray[1]; 425 | var roperp = orig[1] * ray[0] - orig[0] * ray[1]; 426 | var a = q0perp - 2 * q1perp + q2perp; 427 | var b = q1perp - q0perp; 428 | var c = q0perp - roperp; 429 | float s0 = 0; 430 | float s1 = 0; 431 | var num_s = 0; 432 | if (a != 0.0) 433 | { 434 | var discr = b * b - a * c; 435 | if (discr > 0.0) 436 | { 437 | var rcpna = -1 / a; 438 | var d = (float)CRuntime.sqrt(discr); 439 | s0 = (b + d) * rcpna; 440 | s1 = (b - d) * rcpna; 441 | if (s0 >= 0.0 && s0 <= 1.0) 442 | num_s = 1; 443 | if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) 444 | { 445 | if (num_s == 0) 446 | s0 = s1; 447 | ++num_s; 448 | } 449 | } 450 | } 451 | else 452 | { 453 | s0 = c / (-2 * b); 454 | if (s0 >= 0.0 && s0 <= 1.0) 455 | num_s = 1; 456 | } 457 | 458 | if (num_s == 0) return 0; 459 | 460 | var rcp_len2 = 1 / (ray[0] * ray[0] + ray[1] * ray[1]); 461 | var rayn_x = ray[0] * rcp_len2; 462 | var rayn_y = ray[1] * rcp_len2; 463 | var q0d = q0[0] * rayn_x + q0[1] * rayn_y; 464 | var q1d = q1[0] * rayn_x + q1[1] * rayn_y; 465 | var q2d = q2[0] * rayn_x + q2[1] * rayn_y; 466 | var rod = orig[0] * rayn_x + orig[1] * rayn_y; 467 | var q10d = q1d - q0d; 468 | var q20d = q2d - q0d; 469 | var q0rd = q0d - rod; 470 | hits[0] = q0rd + s0 * (2.0f - 2.0f * s0) * q10d + s0 * s0 * q20d; 471 | hits[1] = a * s0 + b; 472 | if (num_s > 1) 473 | { 474 | hits[2] = q0rd + s1 * (2.0f - 2.0f * s1) * q10d + s1 * s1 * q20d; 475 | hits[3] = a * s1 + b; 476 | return 2; 477 | } 478 | 479 | return 1; 480 | } 481 | 482 | public static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) 483 | { 484 | return (top_width + bottom_width) / 2.0f * height; 485 | } 486 | 487 | public static float stbtt__sized_triangle_area(float height, float width) 488 | { 489 | return height * width / 2; 490 | } 491 | 492 | public static int stbtt__solve_cubic(float a, float b, float c, float* r) 493 | { 494 | var s = -a / 3; 495 | var p = b - a * a / 3; 496 | var q = a * (2 * a * a - 9 * b) / 27 + c; 497 | var p3 = p * p * p; 498 | var d = q * q + 4 * p3 / 27; 499 | if (d >= 0) 500 | { 501 | var z = (float)CRuntime.sqrt(d); 502 | var u = (-q + z) / 2; 503 | var v = (-q - z) / 2; 504 | u = stbtt__cuberoot(u); 505 | v = stbtt__cuberoot(v); 506 | r[0] = s + u + v; 507 | return 1; 508 | } 509 | else 510 | { 511 | var u = (float)CRuntime.sqrt(-p / 3); 512 | var v = (float)CRuntime.acos(-CRuntime.sqrt(-27 / p3) * q / 2) / 3; 513 | var m = (float)CRuntime.cos(v); 514 | var n = (float)CRuntime.cos(v - 3.141592 / 2) * 1.732050808f; 515 | r[0] = s + u * 2 * m; 516 | r[1] = s - u * (m + n); 517 | r[2] = s - u * (m - n); 518 | return 3; 519 | } 520 | } 521 | 522 | public static void stbtt__tesselate_cubic(stbtt__point* points, int* num_points, float x0, float y0, float x1, 523 | float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) 524 | { 525 | var dx0 = x1 - x0; 526 | var dy0 = y1 - y0; 527 | var dx1 = x2 - x1; 528 | var dy1 = y2 - y1; 529 | var dx2 = x3 - x2; 530 | var dy2 = y3 - y2; 531 | var dx = x3 - x0; 532 | var dy = y3 - y0; 533 | var longlen = (float)(CRuntime.sqrt(dx0 * dx0 + dy0 * dy0) + CRuntime.sqrt(dx1 * dx1 + dy1 * dy1) + 534 | CRuntime.sqrt(dx2 * dx2 + dy2 * dy2)); 535 | var shortlen = (float)CRuntime.sqrt(dx * dx + dy * dy); 536 | var flatness_squared = longlen * longlen - shortlen * shortlen; 537 | if (n > 16) 538 | return; 539 | if (flatness_squared > objspace_flatness_squared) 540 | { 541 | var x01 = (x0 + x1) / 2; 542 | var y01 = (y0 + y1) / 2; 543 | var x12 = (x1 + x2) / 2; 544 | var y12 = (y1 + y2) / 2; 545 | var x23 = (x2 + x3) / 2; 546 | var y23 = (y2 + y3) / 2; 547 | var xa = (x01 + x12) / 2; 548 | var ya = (y01 + y12) / 2; 549 | var xb = (x12 + x23) / 2; 550 | var yb = (y12 + y23) / 2; 551 | var mx = (xa + xb) / 2; 552 | var my = (ya + yb) / 2; 553 | stbtt__tesselate_cubic(points, num_points, x0, y0, x01, y01, xa, ya, mx, my, objspace_flatness_squared, 554 | n + 1); 555 | stbtt__tesselate_cubic(points, num_points, mx, my, xb, yb, x23, y23, x3, y3, objspace_flatness_squared, 556 | n + 1); 557 | } 558 | else 559 | { 560 | stbtt__add_point(points, *num_points, x3, y3); 561 | *num_points = *num_points + 1; 562 | } 563 | } 564 | 565 | public static int stbtt__tesselate_curve(stbtt__point* points, int* num_points, float x0, float y0, float x1, 566 | float y1, float x2, float y2, float objspace_flatness_squared, int n) 567 | { 568 | var mx = (x0 + 2 * x1 + x2) / 4; 569 | var my = (y0 + 2 * y1 + y2) / 4; 570 | var dx = (x0 + x2) / 2 - mx; 571 | var dy = (y0 + y2) / 2 - my; 572 | if (n > 16) 573 | return 1; 574 | if (dx * dx + dy * dy > objspace_flatness_squared) 575 | { 576 | stbtt__tesselate_curve(points, num_points, x0, y0, (x0 + x1) / 2.0f, (y0 + y1) / 2.0f, mx, my, 577 | objspace_flatness_squared, n + 1); 578 | stbtt__tesselate_curve(points, num_points, mx, my, (x1 + x2) / 2.0f, (y1 + y2) / 2.0f, x2, y2, 579 | objspace_flatness_squared, n + 1); 580 | } 581 | else 582 | { 583 | stbtt__add_point(points, *num_points, x2, y2); 584 | *num_points = *num_points + 1; 585 | } 586 | 587 | return 1; 588 | } 589 | 590 | public static void stbtt__v_prefilter(byte* pixels, int w, int h, int stride_in_bytes, uint kernel_width) 591 | { 592 | var buffer = stackalloc byte[8]; 593 | var safe_h = (int)(h - kernel_width); 594 | var j = 0; 595 | CRuntime.memset(buffer, 0, (ulong)8); 596 | for (j = 0; j < w; ++j) 597 | { 598 | var i = 0; 599 | uint total = 0; 600 | CRuntime.memset(buffer, 0, (ulong)kernel_width); 601 | total = 0; 602 | switch (kernel_width) 603 | { 604 | case 2: 605 | for (i = 0; i <= safe_h; ++i) 606 | { 607 | total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); 608 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i * stride_in_bytes]; 609 | pixels[i * stride_in_bytes] = (byte)(total / 2); 610 | } 611 | 612 | break; 613 | case 3: 614 | for (i = 0; i <= safe_h; ++i) 615 | { 616 | total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); 617 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i * stride_in_bytes]; 618 | pixels[i * stride_in_bytes] = (byte)(total / 3); 619 | } 620 | 621 | break; 622 | case 4: 623 | for (i = 0; i <= safe_h; ++i) 624 | { 625 | total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); 626 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i * stride_in_bytes]; 627 | pixels[i * stride_in_bytes] = (byte)(total / 4); 628 | } 629 | 630 | break; 631 | case 5: 632 | for (i = 0; i <= safe_h; ++i) 633 | { 634 | total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); 635 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i * stride_in_bytes]; 636 | pixels[i * stride_in_bytes] = (byte)(total / 5); 637 | } 638 | 639 | break; 640 | default: 641 | for (i = 0; i <= safe_h; ++i) 642 | { 643 | total += (uint)(pixels[i * stride_in_bytes] - buffer[i & (8 - 1)]); 644 | buffer[(i + kernel_width) & (8 - 1)] = pixels[i * stride_in_bytes]; 645 | pixels[i * stride_in_bytes] = (byte)(total / kernel_width); 646 | } 647 | 648 | break; 649 | } 650 | 651 | for (; i < h; ++i) 652 | { 653 | total -= buffer[i & (8 - 1)]; 654 | pixels[i * stride_in_bytes] = (byte)(total / kernel_width); 655 | } 656 | 657 | pixels += 1; 658 | } 659 | } 660 | 661 | public static int stbtt_BakeFontBitmap(byte* data, int offset, float pixel_height, byte* pixels, int pw, int ph, 662 | int first_char, int num_chars, stbtt_bakedchar* chardata) 663 | { 664 | return stbtt_BakeFontBitmap_internal(data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, 665 | chardata); 666 | } 667 | 668 | public static int stbtt_BakeFontBitmap_internal(byte* data, int offset, float pixel_height, byte* pixels, 669 | int pw, int ph, int first_char, int num_chars, stbtt_bakedchar* chardata) 670 | { 671 | float scale = 0; 672 | var x = 0; 673 | var y = 0; 674 | var bottom_y = 0; 675 | var i = 0; 676 | var f = new stbtt_fontinfo(); 677 | f.userdata = null; 678 | if (stbtt_InitFont(f, data, offset) == 0) 679 | return -1; 680 | CRuntime.memset(pixels, 0, (ulong)(pw * ph)); 681 | x = y = 1; 682 | bottom_y = 1; 683 | scale = stbtt_ScaleForPixelHeight(f, pixel_height); 684 | for (i = 0; i < num_chars; ++i) 685 | { 686 | var advance = 0; 687 | var lsb = 0; 688 | var x0 = 0; 689 | var y0 = 0; 690 | var x1 = 0; 691 | var y1 = 0; 692 | var gw = 0; 693 | var gh = 0; 694 | var g = stbtt_FindGlyphIndex(f, first_char + i); 695 | stbtt_GetGlyphHMetrics(f, g, &advance, &lsb); 696 | stbtt_GetGlyphBitmapBox(f, g, scale, scale, &x0, &y0, &x1, &y1); 697 | gw = x1 - x0; 698 | gh = y1 - y0; 699 | if (x + gw + 1 >= pw) 700 | { 701 | y = bottom_y; 702 | x = 1; 703 | } 704 | 705 | if (y + gh + 1 >= ph) 706 | return -i; 707 | stbtt_MakeGlyphBitmap(f, pixels + x + y * pw, gw, gh, pw, scale, scale, g); 708 | chardata[i].x0 = (ushort)(short)x; 709 | chardata[i].y0 = (ushort)(short)y; 710 | chardata[i].x1 = (ushort)(short)(x + gw); 711 | chardata[i].y1 = (ushort)(short)(y + gh); 712 | chardata[i].xadvance = scale * advance; 713 | chardata[i].xoff = x0; 714 | chardata[i].yoff = y0; 715 | x = x + gw + 1; 716 | if (y + gh + 1 > bottom_y) 717 | bottom_y = y + gh + 1; 718 | } 719 | 720 | return bottom_y; 721 | } 722 | 723 | public static int stbtt_CompareUTF8toUTF16_bigendian(sbyte* s1, int len1, sbyte* s2, int len2) 724 | { 725 | return stbtt_CompareUTF8toUTF16_bigendian_internal(s1, len1, s2, len2); 726 | } 727 | 728 | public static int stbtt_CompareUTF8toUTF16_bigendian_internal(sbyte* s1, int len1, sbyte* s2, int len2) 729 | { 730 | return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((byte*)s1, len1, (byte*)s2, len2) ? 1 : 0; 731 | } 732 | 733 | public static int stbtt_FindMatchingFont(byte* fontdata, sbyte* name, int flags) 734 | { 735 | return stbtt_FindMatchingFont_internal(fontdata, name, flags); 736 | } 737 | 738 | public static int stbtt_FindMatchingFont_internal(byte* font_collection, sbyte* name_utf8, int flags) 739 | { 740 | var i = 0; 741 | for (i = 0; ; ++i) 742 | { 743 | var off = stbtt_GetFontOffsetForIndex(font_collection, i); 744 | if (off < 0) 745 | return off; 746 | if (stbtt__matches(font_collection, (uint)off, (byte*)name_utf8, flags) != 0) 747 | return off; 748 | } 749 | } 750 | 751 | public static stbtt__point* stbtt_FlattenCurves(stbtt_vertex* vertices, int num_verts, float objspace_flatness, 752 | int** contour_lengths, int* num_contours, void* userdata) 753 | { 754 | stbtt__point* points = null; 755 | var num_points = 0; 756 | var objspace_flatness_squared = objspace_flatness * objspace_flatness; 757 | var i = 0; 758 | var n = 0; 759 | var start = 0; 760 | var pass = 0; 761 | for (i = 0; i < num_verts; ++i) 762 | if (vertices[i].type == STBTT_vmove) 763 | ++n; 764 | 765 | *num_contours = n; 766 | if (n == 0) 767 | return null; 768 | *contour_lengths = (int*)CRuntime.malloc((ulong)(sizeof(int) * n)); 769 | if (*contour_lengths == null) 770 | { 771 | *num_contours = 0; 772 | return null; 773 | } 774 | 775 | for (pass = 0; pass < 2; ++pass) 776 | { 777 | float x = 0; 778 | float y = 0; 779 | if (pass == 1) 780 | { 781 | points = (stbtt__point*)CRuntime.malloc((ulong)(num_points * sizeof(stbtt__point))); 782 | if (points == null) 783 | goto error; 784 | } 785 | 786 | num_points = 0; 787 | n = -1; 788 | for (i = 0; i < num_verts; ++i) 789 | switch (vertices[i].type) 790 | { 791 | case STBTT_vmove: 792 | if (n >= 0) 793 | (*contour_lengths)[n] = num_points - start; 794 | ++n; 795 | start = num_points; 796 | x = vertices[i].x; 797 | y = vertices[i].y; 798 | stbtt__add_point(points, num_points++, x, y); 799 | break; 800 | case STBTT_vline: 801 | x = vertices[i].x; 802 | y = vertices[i].y; 803 | stbtt__add_point(points, num_points++, x, y); 804 | break; 805 | case STBTT_vcurve: 806 | stbtt__tesselate_curve(points, &num_points, x, y, vertices[i].cx, vertices[i].cy, 807 | vertices[i].x, vertices[i].y, objspace_flatness_squared, 0); 808 | x = vertices[i].x; 809 | y = vertices[i].y; 810 | break; 811 | case STBTT_vcubic: 812 | stbtt__tesselate_cubic(points, &num_points, x, y, vertices[i].cx, vertices[i].cy, 813 | vertices[i].cx1, vertices[i].cy1, vertices[i].x, vertices[i].y, 814 | objspace_flatness_squared, 0); 815 | x = vertices[i].x; 816 | y = vertices[i].y; 817 | break; 818 | } 819 | 820 | (*contour_lengths)[n] = num_points - start; 821 | } 822 | 823 | return points; 824 | error:; 825 | CRuntime.free(points); 826 | CRuntime.free(*contour_lengths); 827 | *contour_lengths = null; 828 | *num_contours = 0; 829 | return null; 830 | } 831 | 832 | public static void stbtt_FreeBitmap(byte* bitmap, void* userdata) 833 | { 834 | CRuntime.free(bitmap); 835 | } 836 | 837 | public static void stbtt_FreeSDF(byte* bitmap, void* userdata) 838 | { 839 | CRuntime.free(bitmap); 840 | } 841 | 842 | public static void stbtt_GetBakedQuad(stbtt_bakedchar* chardata, int pw, int ph, int char_index, float* xpos, 843 | float* ypos, stbtt_aligned_quad* q, int opengl_fillrule) 844 | { 845 | var d3d_bias = opengl_fillrule != 0 ? 0 : -0.5f; 846 | var ipw = 1.0f / pw; 847 | var iph = 1.0f / ph; 848 | var b = chardata + char_index; 849 | var round_x = (int)CRuntime.floor(*xpos + b->xoff + 0.5f); 850 | var round_y = (int)CRuntime.floor(*ypos + b->yoff + 0.5f); 851 | q->x0 = round_x + d3d_bias; 852 | q->y0 = round_y + d3d_bias; 853 | q->x1 = round_x + b->x1 - b->x0 + d3d_bias; 854 | q->y1 = round_y + b->y1 - b->y0 + d3d_bias; 855 | q->s0 = b->x0 * ipw; 856 | q->t0 = b->y0 * iph; 857 | q->s1 = b->x1 * ipw; 858 | q->t1 = b->y1 * iph; 859 | *xpos += b->xadvance; 860 | } 861 | 862 | public static int stbtt_GetFontOffsetForIndex(byte* data, int index) 863 | { 864 | return stbtt_GetFontOffsetForIndex_internal(data, index); 865 | } 866 | 867 | public static int stbtt_GetFontOffsetForIndex_internal(byte* font_collection, int index) 868 | { 869 | if (stbtt__isfont(font_collection) != 0) 870 | return index == 0 ? 0 : -1; 871 | if (font_collection[0] == "ttcf"[0] && font_collection[1] == "ttcf"[1] && font_collection[2] == "ttcf"[2] && 872 | font_collection[3] == "ttcf"[3]) 873 | if (ttULONG(font_collection + 4) == 0x00010000 || ttULONG(font_collection + 4) == 0x00020000) 874 | { 875 | var n = ttLONG(font_collection + 8); 876 | if (index >= n) 877 | return -1; 878 | return (int)ttULONG(font_collection + 12 + index * 4); 879 | } 880 | 881 | return -1; 882 | } 883 | 884 | public static int stbtt_GetNumberOfFonts(byte* data) 885 | { 886 | return stbtt_GetNumberOfFonts_internal(data); 887 | } 888 | 889 | public static int stbtt_GetNumberOfFonts_internal(byte* font_collection) 890 | { 891 | if (stbtt__isfont(font_collection) != 0) 892 | return 1; 893 | if (font_collection[0] == "ttcf"[0] && font_collection[1] == "ttcf"[1] && font_collection[2] == "ttcf"[2] && 894 | font_collection[3] == "ttcf"[3]) 895 | if (ttULONG(font_collection + 4) == 0x00010000 || ttULONG(font_collection + 4) == 0x00020000) 896 | return ttLONG(font_collection + 8); 897 | 898 | return 0; 899 | } 900 | 901 | public static void stbtt_GetPackedQuad(stbtt_packedchar* chardata, int pw, int ph, int char_index, float* xpos, 902 | float* ypos, stbtt_aligned_quad* q, int align_to_integer) 903 | { 904 | var ipw = 1.0f / pw; 905 | var iph = 1.0f / ph; 906 | var b = chardata + char_index; 907 | if (align_to_integer != 0) 908 | { 909 | float x = (int)CRuntime.floor(*xpos + b->xoff + 0.5f); 910 | float y = (int)CRuntime.floor(*ypos + b->yoff + 0.5f); 911 | q->x0 = x; 912 | q->y0 = y; 913 | q->x1 = x + b->xoff2 - b->xoff; 914 | q->y1 = y + b->yoff2 - b->yoff; 915 | } 916 | else 917 | { 918 | q->x0 = *xpos + b->xoff; 919 | q->y0 = *ypos + b->yoff; 920 | q->x1 = *xpos + b->xoff2; 921 | q->y1 = *ypos + b->yoff2; 922 | } 923 | 924 | q->s0 = b->x0 * ipw; 925 | q->t0 = b->y0 * iph; 926 | q->s1 = b->x1 * ipw; 927 | q->t1 = b->y1 * iph; 928 | *xpos += b->xadvance; 929 | } 930 | 931 | public static void stbtt_GetScaledFontVMetrics(byte* fontdata, int index, float size, float* ascent, 932 | float* descent, float* lineGap) 933 | { 934 | var i_ascent = 0; 935 | var i_descent = 0; 936 | var i_lineGap = 0; 937 | float scale = 0; 938 | var info = new stbtt_fontinfo(); 939 | stbtt_InitFont(info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); 940 | scale = size > 0 ? stbtt_ScaleForPixelHeight(info, size) : stbtt_ScaleForMappingEmToPixels(info, -size); 941 | stbtt_GetFontVMetrics(info, &i_ascent, &i_descent, &i_lineGap); 942 | *ascent = i_ascent * scale; 943 | *descent = i_descent * scale; 944 | *lineGap = i_lineGap * scale; 945 | } 946 | 947 | public static void stbtt_setvertex(stbtt_vertex* v, byte type, int x, int y, int cx, int cy) 948 | { 949 | v->type = type; 950 | v->x = (short)x; 951 | v->y = (short)y; 952 | v->cx = (short)cx; 953 | v->cy = (short)cy; 954 | } 955 | 956 | public static int ttLONG(byte* p) 957 | { 958 | return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; 959 | } 960 | 961 | public static short ttSHORT(byte* p) 962 | { 963 | return (short)(p[0] * 256 + p[1]); 964 | } 965 | 966 | public static uint ttULONG(byte* p) 967 | { 968 | return (uint)((p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]); 969 | } 970 | 971 | public static ushort ttUSHORT(byte* p) 972 | { 973 | return (ushort)(p[0] * 256 + p[1]); 974 | } 975 | 976 | [StructLayout(LayoutKind.Sequential)] 977 | public struct stbtt__point 978 | { 979 | public float x; 980 | public float y; 981 | } 982 | 983 | [StructLayout(LayoutKind.Sequential)] 984 | public struct stbtt_aligned_quad 985 | { 986 | public float x0; 987 | public float y0; 988 | public float s0; 989 | public float t0; 990 | public float x1; 991 | public float y1; 992 | public float s1; 993 | public float t1; 994 | } 995 | 996 | [StructLayout(LayoutKind.Sequential)] 997 | public struct stbtt_bakedchar 998 | { 999 | public ushort x0; 1000 | public ushort y0; 1001 | public ushort x1; 1002 | public ushort y1; 1003 | public float xoff; 1004 | public float yoff; 1005 | public float xadvance; 1006 | } 1007 | 1008 | [StructLayout(LayoutKind.Sequential)] 1009 | public struct stbtt_packedchar 1010 | { 1011 | public ushort x0; 1012 | public ushort y0; 1013 | public ushort x1; 1014 | public ushort y1; 1015 | public float xoff; 1016 | public float yoff; 1017 | public float xadvance; 1018 | public float xoff2; 1019 | public float yoff2; 1020 | } 1021 | 1022 | [StructLayout(LayoutKind.Sequential)] 1023 | public struct stbtt_vertex 1024 | { 1025 | public short x; 1026 | public short y; 1027 | public short cx; 1028 | public short cy; 1029 | public short cx1; 1030 | public short cy1; 1031 | public byte type; 1032 | public byte padding; 1033 | } 1034 | } 1035 | } -------------------------------------------------------------------------------- /src/StbTrueType.Generated.FontInfo.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using Hebron.Runtime; 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace StbTrueTypeSharp 8 | { 9 | unsafe partial class StbTrueType 10 | { 11 | public static stbtt__buf stbtt__cid_get_glyph_subrs(stbtt_fontinfo info, int glyph_index) 12 | { 13 | var fdselect = info.fdselect; 14 | var nranges = 0; 15 | var start = 0; 16 | var end = 0; 17 | var v = 0; 18 | var fmt = 0; 19 | var fdselector = -1; 20 | var i = 0; 21 | stbtt__buf_seek(&fdselect, 0); 22 | fmt = stbtt__buf_get8(&fdselect); 23 | if (fmt == 0) 24 | { 25 | stbtt__buf_skip(&fdselect, glyph_index); 26 | fdselector = stbtt__buf_get8(&fdselect); 27 | } 28 | else if (fmt == 3) 29 | { 30 | nranges = (int)stbtt__buf_get(&fdselect, 2); 31 | start = (int)stbtt__buf_get(&fdselect, 2); 32 | for (i = 0; i < nranges; i++) 33 | { 34 | v = stbtt__buf_get8(&fdselect); 35 | end = (int)stbtt__buf_get(&fdselect, 2); 36 | if (glyph_index >= start && glyph_index < end) 37 | { 38 | fdselector = v; 39 | break; 40 | } 41 | 42 | start = end; 43 | } 44 | } 45 | 46 | if (fdselector == -1) 47 | stbtt__new_buf(null, 0); 48 | return stbtt__get_subrs(info.cff, stbtt__cff_index_get(info.fontdicts, fdselector)); 49 | } 50 | 51 | public static int stbtt__close_shape(stbtt_vertex* vertices, int num_vertices, int was_off, int start_off, 52 | int sx, int sy, int scx, int scy, int cx, int cy) 53 | { 54 | if (start_off != 0) 55 | { 56 | if (was_off != 0) 57 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx + scx) >> 1, (cy + scy) >> 1, cx, cy); 58 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx, sy, scx, scy); 59 | } 60 | else 61 | { 62 | if (was_off != 0) 63 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx, sy, cx, cy); 64 | else 65 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, sx, sy, 0, 0); 66 | } 67 | 68 | return num_vertices; 69 | } 70 | 71 | public static int stbtt__get_svg(stbtt_fontinfo info) 72 | { 73 | uint t = 0; 74 | if (info.svg < 0) 75 | { 76 | t = stbtt__find_table(info.data, (uint)info.fontstart, "SVG "); 77 | if (t != 0) 78 | { 79 | var offset = ttULONG(info.data + t + 2); 80 | info.svg = (int)(t + offset); 81 | } 82 | else 83 | { 84 | info.svg = 0; 85 | } 86 | } 87 | 88 | return info.svg; 89 | } 90 | 91 | public static int stbtt__GetCoverageIndex(byte* coverageTable, int glyph) 92 | { 93 | var coverageFormat = ttUSHORT(coverageTable); 94 | switch (coverageFormat) 95 | { 96 | case 1: 97 | { 98 | var glyphCount = ttUSHORT(coverageTable + 2); 99 | var l = 0; 100 | var r = glyphCount - 1; 101 | var m = 0; 102 | var straw = 0; 103 | var needle = glyph; 104 | while (l <= r) 105 | { 106 | var glyphArray = coverageTable + 4; 107 | ushort glyphID = 0; 108 | m = (l + r) >> 1; 109 | glyphID = ttUSHORT(glyphArray + 2 * m); 110 | straw = glyphID; 111 | if (needle < straw) 112 | r = m - 1; 113 | else if (needle > straw) 114 | l = m + 1; 115 | else 116 | return m; 117 | } 118 | 119 | break; 120 | } 121 | 122 | case 2: 123 | { 124 | var rangeCount = ttUSHORT(coverageTable + 2); 125 | var rangeArray = coverageTable + 4; 126 | var l = 0; 127 | var r = rangeCount - 1; 128 | var m = 0; 129 | var strawStart = 0; 130 | var strawEnd = 0; 131 | var needle = glyph; 132 | while (l <= r) 133 | { 134 | byte* rangeRecord; 135 | m = (l + r) >> 1; 136 | rangeRecord = rangeArray + 6 * m; 137 | strawStart = ttUSHORT(rangeRecord); 138 | strawEnd = ttUSHORT(rangeRecord + 2); 139 | if (needle < strawStart) 140 | { 141 | r = m - 1; 142 | } 143 | else if (needle > strawEnd) 144 | { 145 | l = m + 1; 146 | } 147 | else 148 | { 149 | var startCoverageIndex = ttUSHORT(rangeRecord + 4); 150 | return startCoverageIndex + glyph - strawStart; 151 | } 152 | } 153 | 154 | break; 155 | } 156 | 157 | default: 158 | return -1; 159 | } 160 | 161 | return -1; 162 | } 163 | 164 | public static int stbtt__GetGlyfOffset(stbtt_fontinfo info, int glyph_index) 165 | { 166 | var g1 = 0; 167 | var g2 = 0; 168 | if (glyph_index >= info.numGlyphs) 169 | return -1; 170 | if (info.indexToLocFormat >= 2) 171 | return -1; 172 | if (info.indexToLocFormat == 0) 173 | { 174 | g1 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2) * 2; 175 | g2 = info.glyf + ttUSHORT(info.data + info.loca + glyph_index * 2 + 2) * 2; 176 | } 177 | else 178 | { 179 | g1 = (int)(info.glyf + ttULONG(info.data + info.loca + glyph_index * 4)); 180 | g2 = (int)(info.glyf + ttULONG(info.data + info.loca + glyph_index * 4 + 4)); 181 | } 182 | 183 | return g1 == g2 ? -1 : g1; 184 | } 185 | 186 | public static int stbtt__GetGlyphClass(byte* classDefTable, int glyph) 187 | { 188 | var classDefFormat = ttUSHORT(classDefTable); 189 | switch (classDefFormat) 190 | { 191 | case 1: 192 | { 193 | var startGlyphID = ttUSHORT(classDefTable + 2); 194 | var glyphCount = ttUSHORT(classDefTable + 4); 195 | var classDef1ValueArray = classDefTable + 6; 196 | if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) 197 | return ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); 198 | break; 199 | } 200 | 201 | case 2: 202 | { 203 | var classRangeCount = ttUSHORT(classDefTable + 2); 204 | var classRangeRecords = classDefTable + 4; 205 | var l = 0; 206 | var r = classRangeCount - 1; 207 | var m = 0; 208 | var strawStart = 0; 209 | var strawEnd = 0; 210 | var needle = glyph; 211 | while (l <= r) 212 | { 213 | byte* classRangeRecord; 214 | m = (l + r) >> 1; 215 | classRangeRecord = classRangeRecords + 6 * m; 216 | strawStart = ttUSHORT(classRangeRecord); 217 | strawEnd = ttUSHORT(classRangeRecord + 2); 218 | if (needle < strawStart) 219 | r = m - 1; 220 | else if (needle > strawEnd) 221 | l = m + 1; 222 | else 223 | return ttUSHORT(classRangeRecord + 4); 224 | } 225 | 226 | break; 227 | } 228 | 229 | default: 230 | return -1; 231 | } 232 | 233 | return 0; 234 | } 235 | 236 | public static int stbtt__GetGlyphGPOSInfoAdvance(stbtt_fontinfo info, int glyph1, int glyph2) 237 | { 238 | ushort lookupListOffset = 0; 239 | byte* lookupList; 240 | ushort lookupCount = 0; 241 | byte* data; 242 | var i = 0; 243 | var sti = 0; 244 | if (info.gpos == 0) 245 | return 0; 246 | data = info.data + info.gpos; 247 | if (ttUSHORT(data + 0) != 1) 248 | return 0; 249 | if (ttUSHORT(data + 2) != 0) 250 | return 0; 251 | lookupListOffset = ttUSHORT(data + 8); 252 | lookupList = data + lookupListOffset; 253 | lookupCount = ttUSHORT(lookupList); 254 | for (i = 0; i < lookupCount; ++i) 255 | { 256 | var lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); 257 | var lookupTable = lookupList + lookupOffset; 258 | var lookupType = ttUSHORT(lookupTable); 259 | var subTableCount = ttUSHORT(lookupTable + 4); 260 | var subTableOffsets = lookupTable + 6; 261 | if (lookupType != 2) 262 | continue; 263 | for (sti = 0; sti < subTableCount; sti++) 264 | { 265 | var subtableOffset = ttUSHORT(subTableOffsets + 2 * sti); 266 | var table = lookupTable + subtableOffset; 267 | var posFormat = ttUSHORT(table); 268 | var coverageOffset = ttUSHORT(table + 2); 269 | var coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1); 270 | if (coverageIndex == -1) 271 | continue; 272 | switch (posFormat) 273 | { 274 | case 1: 275 | { 276 | var l = 0; 277 | var r = 0; 278 | var m = 0; 279 | var straw = 0; 280 | var needle = 0; 281 | var valueFormat1 = ttUSHORT(table + 4); 282 | var valueFormat2 = ttUSHORT(table + 6); 283 | if (valueFormat1 == 4 && valueFormat2 == 0) 284 | { 285 | var valueRecordPairSizeInBytes = 2; 286 | var pairSetCount = ttUSHORT(table + 8); 287 | var pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); 288 | var pairValueTable = table + pairPosOffset; 289 | var pairValueCount = ttUSHORT(pairValueTable); 290 | var pairValueArray = pairValueTable + 2; 291 | if (coverageIndex >= pairSetCount) 292 | return 0; 293 | needle = glyph2; 294 | r = pairValueCount - 1; 295 | l = 0; 296 | while (l <= r) 297 | { 298 | ushort secondGlyph = 0; 299 | byte* pairValue; 300 | m = (l + r) >> 1; 301 | pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; 302 | secondGlyph = ttUSHORT(pairValue); 303 | straw = secondGlyph; 304 | if (needle < straw) 305 | { 306 | r = m - 1; 307 | } 308 | else if (needle > straw) 309 | { 310 | l = m + 1; 311 | } 312 | else 313 | { 314 | var xAdvance = ttSHORT(pairValue + 2); 315 | return xAdvance; 316 | } 317 | } 318 | } 319 | else 320 | { 321 | return 0; 322 | } 323 | 324 | break; 325 | } 326 | 327 | case 2: 328 | { 329 | var valueFormat1 = ttUSHORT(table + 4); 330 | var valueFormat2 = ttUSHORT(table + 6); 331 | if (valueFormat1 == 4 && valueFormat2 == 0) 332 | { 333 | var classDef1Offset = ttUSHORT(table + 8); 334 | var classDef2Offset = ttUSHORT(table + 10); 335 | var glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); 336 | var glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); 337 | var class1Count = ttUSHORT(table + 12); 338 | var class2Count = ttUSHORT(table + 14); 339 | byte* class1Records; 340 | byte* class2Records; 341 | short xAdvance = 0; 342 | if (glyph1class < 0 || glyph1class >= class1Count) 343 | return 0; 344 | if (glyph2class < 0 || glyph2class >= class2Count) 345 | return 0; 346 | class1Records = table + 16; 347 | class2Records = class1Records + 2 * glyph1class * class2Count; 348 | xAdvance = ttSHORT(class2Records + 2 * glyph2class); 349 | return xAdvance; 350 | } 351 | 352 | return 0; 353 | } 354 | 355 | default: 356 | return 0; 357 | } 358 | } 359 | } 360 | 361 | return 0; 362 | } 363 | 364 | public static int stbtt__GetGlyphInfoT2(stbtt_fontinfo info, int glyph_index, int* x0, int* y0, int* x1, 365 | int* y1) 366 | { 367 | var c = new stbtt__csctx(); 368 | c.bounds = 1; 369 | var r = stbtt__run_charstring(info, glyph_index, &c); 370 | if (x0 != null) 371 | *x0 = r != 0 ? c.min_x : 0; 372 | if (y0 != null) 373 | *y0 = r != 0 ? c.min_y : 0; 374 | if (x1 != null) 375 | *x1 = r != 0 ? c.max_x : 0; 376 | if (y1 != null) 377 | *y1 = r != 0 ? c.max_y : 0; 378 | return r != 0 ? c.num_vertices : 0; 379 | } 380 | 381 | public static int stbtt__GetGlyphKernInfoAdvance(stbtt_fontinfo info, int glyph1, int glyph2) 382 | { 383 | var data = info.data + info.kern; 384 | uint needle = 0; 385 | uint straw = 0; 386 | var l = 0; 387 | var r = 0; 388 | var m = 0; 389 | if (info.kern == 0) 390 | return 0; 391 | if (ttUSHORT(data + 2) < 1) 392 | return 0; 393 | if (ttUSHORT(data + 8) != 1) 394 | return 0; 395 | l = 0; 396 | r = ttUSHORT(data + 10) - 1; 397 | needle = (uint)((glyph1 << 16) | glyph2); 398 | while (l <= r) 399 | { 400 | m = (l + r) >> 1; 401 | straw = ttULONG(data + 18 + m * 6); 402 | if (needle < straw) 403 | r = m - 1; 404 | else if (needle > straw) 405 | l = m + 1; 406 | else 407 | return ttSHORT(data + 22 + m * 6); 408 | } 409 | 410 | return 0; 411 | } 412 | 413 | public static int stbtt__GetGlyphShapeT2(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) 414 | { 415 | var count_ctx = new stbtt__csctx(); 416 | count_ctx.bounds = 1; 417 | var output_ctx = new stbtt__csctx(); 418 | if (stbtt__run_charstring(info, glyph_index, &count_ctx) != 0) 419 | { 420 | *pvertices = (stbtt_vertex*)CRuntime.malloc((ulong)(count_ctx.num_vertices * sizeof(stbtt_vertex))); 421 | output_ctx.pvertices = *pvertices; 422 | if (stbtt__run_charstring(info, glyph_index, &output_ctx) != 0) return output_ctx.num_vertices; 423 | } 424 | 425 | *pvertices = null; 426 | return 0; 427 | } 428 | 429 | public static int stbtt__GetGlyphShapeTT(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) 430 | { 431 | short numberOfContours = 0; 432 | byte* endPtsOfContours; 433 | var data = info.data; 434 | stbtt_vertex* vertices = null; 435 | var num_vertices = 0; 436 | var g = stbtt__GetGlyfOffset(info, glyph_index); 437 | *pvertices = null; 438 | if (g < 0) 439 | return 0; 440 | numberOfContours = ttSHORT(data + g); 441 | if (numberOfContours > 0) 442 | { 443 | byte flags = 0; 444 | byte flagcount = 0; 445 | var ins = 0; 446 | var i = 0; 447 | var j = 0; 448 | var m = 0; 449 | var n = 0; 450 | var next_move = 0; 451 | var was_off = 0; 452 | var off = 0; 453 | var start_off = 0; 454 | var x = 0; 455 | var y = 0; 456 | var cx = 0; 457 | var cy = 0; 458 | var sx = 0; 459 | var sy = 0; 460 | var scx = 0; 461 | var scy = 0; 462 | byte* points; 463 | endPtsOfContours = data + g + 10; 464 | ins = ttUSHORT(data + g + 10 + numberOfContours * 2); 465 | points = data + g + 10 + numberOfContours * 2 + 2 + ins; 466 | n = 1 + ttUSHORT(endPtsOfContours + numberOfContours * 2 - 2); 467 | m = n + 2 * numberOfContours; 468 | vertices = (stbtt_vertex*)CRuntime.malloc((ulong)(m * sizeof(stbtt_vertex))); 469 | if (vertices == null) 470 | return 0; 471 | next_move = 0; 472 | flagcount = 0; 473 | off = m - n; 474 | for (i = 0; i < n; ++i) 475 | { 476 | if (flagcount == 0) 477 | { 478 | flags = *points++; 479 | if ((flags & 8) != 0) 480 | flagcount = *points++; 481 | } 482 | else 483 | { 484 | --flagcount; 485 | } 486 | 487 | vertices[off + i].type = flags; 488 | } 489 | 490 | x = 0; 491 | for (i = 0; i < n; ++i) 492 | { 493 | flags = vertices[off + i].type; 494 | if ((flags & 2) != 0) 495 | { 496 | short dx = *points++; 497 | x += (flags & 16) != 0 ? dx : -dx; 498 | } 499 | else 500 | { 501 | if ((flags & 16) == 0) 502 | { 503 | x = x + (short)(points[0] * 256 + points[1]); 504 | points += 2; 505 | } 506 | } 507 | 508 | vertices[off + i].x = (short)x; 509 | } 510 | 511 | y = 0; 512 | for (i = 0; i < n; ++i) 513 | { 514 | flags = vertices[off + i].type; 515 | if ((flags & 4) != 0) 516 | { 517 | short dy = *points++; 518 | y += (flags & 32) != 0 ? dy : -dy; 519 | } 520 | else 521 | { 522 | if ((flags & 32) == 0) 523 | { 524 | y = y + (short)(points[0] * 256 + points[1]); 525 | points += 2; 526 | } 527 | } 528 | 529 | vertices[off + i].y = (short)y; 530 | } 531 | 532 | num_vertices = 0; 533 | sx = sy = cx = cy = scx = scy = 0; 534 | for (i = 0; i < n; ++i) 535 | { 536 | flags = vertices[off + i].type; 537 | x = vertices[off + i].x; 538 | y = vertices[off + i].y; 539 | if (next_move == i) 540 | { 541 | if (i != 0) 542 | num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, 543 | scy, cx, cy); 544 | start_off = (flags & 1) != 0 ? 0 : 1; 545 | if (start_off != 0) 546 | { 547 | scx = x; 548 | scy = y; 549 | if ((vertices[off + i + 1].type & 1) == 0) 550 | { 551 | sx = (x + vertices[off + i + 1].x) >> 1; 552 | sy = (y + vertices[off + i + 1].y) >> 1; 553 | } 554 | else 555 | { 556 | sx = vertices[off + i + 1].x; 557 | sy = vertices[off + i + 1].y; 558 | ++i; 559 | } 560 | } 561 | else 562 | { 563 | sx = x; 564 | sy = y; 565 | } 566 | 567 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove, sx, sy, 0, 0); 568 | was_off = 0; 569 | next_move = 1 + ttUSHORT(endPtsOfContours + j * 2); 570 | ++j; 571 | } 572 | else 573 | { 574 | if ((flags & 1) == 0) 575 | { 576 | if (was_off != 0) 577 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx + x) >> 1, (cy + y) >> 1, 578 | cx, cy); 579 | cx = x; 580 | cy = y; 581 | was_off = 1; 582 | } 583 | else 584 | { 585 | if (was_off != 0) 586 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x, y, cx, cy); 587 | else 588 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x, y, 0, 0); 589 | was_off = 0; 590 | } 591 | } 592 | } 593 | 594 | num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx, sy, scx, scy, cx, cy); 595 | } 596 | else if (numberOfContours < 0) 597 | { 598 | var more = 1; 599 | var comp = data + g + 10; 600 | num_vertices = 0; 601 | vertices = null; 602 | while (more != 0) 603 | { 604 | ushort flags = 0; 605 | ushort gidx = 0; 606 | var comp_num_verts = 0; 607 | var i = 0; 608 | stbtt_vertex* comp_verts = null; 609 | stbtt_vertex* tmp = null; 610 | var mtx = stackalloc float[] { 1, 0, 0, 1, 0, 0 }; 611 | float m = 0; 612 | float n = 0; 613 | flags = (ushort)ttSHORT(comp); 614 | comp += 2; 615 | gidx = (ushort)ttSHORT(comp); 616 | comp += 2; 617 | if ((flags & 2) != 0) 618 | { 619 | if ((flags & 1) != 0) 620 | { 621 | mtx[4] = ttSHORT(comp); 622 | comp += 2; 623 | mtx[5] = ttSHORT(comp); 624 | comp += 2; 625 | } 626 | else 627 | { 628 | mtx[4] = *(sbyte*)comp; 629 | comp += 1; 630 | mtx[5] = *(sbyte*)comp; 631 | comp += 1; 632 | } 633 | } 634 | 635 | if ((flags & (1 << 3)) != 0) 636 | { 637 | mtx[0] = mtx[3] = ttSHORT(comp) / 16384.0f; 638 | comp += 2; 639 | mtx[1] = mtx[2] = 0; 640 | } 641 | else if ((flags & (1 << 6)) != 0) 642 | { 643 | mtx[0] = ttSHORT(comp) / 16384.0f; 644 | comp += 2; 645 | mtx[1] = mtx[2] = 0; 646 | mtx[3] = ttSHORT(comp) / 16384.0f; 647 | comp += 2; 648 | } 649 | else if ((flags & (1 << 7)) != 0) 650 | { 651 | mtx[0] = ttSHORT(comp) / 16384.0f; 652 | comp += 2; 653 | mtx[1] = ttSHORT(comp) / 16384.0f; 654 | comp += 2; 655 | mtx[2] = ttSHORT(comp) / 16384.0f; 656 | comp += 2; 657 | mtx[3] = ttSHORT(comp) / 16384.0f; 658 | comp += 2; 659 | } 660 | 661 | m = (float)CRuntime.sqrt(mtx[0] * mtx[0] + mtx[1] * mtx[1]); 662 | n = (float)CRuntime.sqrt(mtx[2] * mtx[2] + mtx[3] * mtx[3]); 663 | comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); 664 | if (comp_num_verts > 0) 665 | { 666 | for (i = 0; i < comp_num_verts; ++i) 667 | { 668 | var v = &comp_verts[i]; 669 | short x = 0; 670 | short y = 0; 671 | x = v->x; 672 | y = v->y; 673 | v->x = (short)(m * (mtx[0] * x + mtx[2] * y + mtx[4])); 674 | v->y = (short)(n * (mtx[1] * x + mtx[3] * y + mtx[5])); 675 | x = v->cx; 676 | y = v->cy; 677 | v->cx = (short)(m * (mtx[0] * x + mtx[2] * y + mtx[4])); 678 | v->cy = (short)(n * (mtx[1] * x + mtx[3] * y + mtx[5])); 679 | } 680 | 681 | tmp = (stbtt_vertex*)CRuntime.malloc((ulong)((num_vertices + comp_num_verts) * 682 | sizeof(stbtt_vertex))); 683 | if (tmp == null) 684 | { 685 | if (vertices != null) 686 | CRuntime.free(vertices); 687 | if (comp_verts != null) 688 | CRuntime.free(comp_verts); 689 | return 0; 690 | } 691 | 692 | if (num_vertices > 0 && vertices != null) 693 | CRuntime.memcpy(tmp, vertices, (ulong)(num_vertices * sizeof(stbtt_vertex))); 694 | CRuntime.memcpy(tmp + num_vertices, comp_verts, 695 | (ulong)(comp_num_verts * sizeof(stbtt_vertex))); 696 | if (vertices != null) 697 | CRuntime.free(vertices); 698 | vertices = tmp; 699 | CRuntime.free(comp_verts); 700 | num_vertices += comp_num_verts; 701 | } 702 | 703 | more = flags & (1 << 5); 704 | } 705 | } 706 | 707 | *pvertices = vertices; 708 | return num_vertices; 709 | } 710 | 711 | public static int stbtt__run_charstring(stbtt_fontinfo info, int glyph_index, stbtt__csctx* c) 712 | { 713 | var in_header = 1; 714 | var maskbits = 0; 715 | var subr_stack_height = 0; 716 | var sp = 0; 717 | var v = 0; 718 | var i = 0; 719 | var b0 = 0; 720 | var has_subrs = 0; 721 | var clear_stack = 0; 722 | var s = stackalloc float[48]; 723 | var subr_stack = stackalloc stbtt__buf[10]; 724 | var subrs = info.subrs; 725 | var b = new stbtt__buf(); 726 | float f = 0; 727 | b = stbtt__cff_index_get(info.charstrings, glyph_index); 728 | while (b.cursor < b.size) 729 | { 730 | i = 0; 731 | clear_stack = 1; 732 | b0 = stbtt__buf_get8(&b); 733 | switch (b0) 734 | { 735 | case 0x13: 736 | case 0x14: 737 | if (in_header != 0) 738 | maskbits += sp / 2; 739 | in_header = 0; 740 | stbtt__buf_skip(&b, (maskbits + 7) / 8); 741 | break; 742 | case 0x01: 743 | case 0x03: 744 | case 0x12: 745 | case 0x17: 746 | maskbits += sp / 2; 747 | break; 748 | case 0x15: 749 | in_header = 0; 750 | if (sp < 2) 751 | return 0; 752 | stbtt__csctx_rmove_to(c, s[sp - 2], s[sp - 1]); 753 | break; 754 | case 0x04: 755 | in_header = 0; 756 | if (sp < 1) 757 | return 0; 758 | stbtt__csctx_rmove_to(c, 0, s[sp - 1]); 759 | break; 760 | case 0x16: 761 | in_header = 0; 762 | if (sp < 1) 763 | return 0; 764 | stbtt__csctx_rmove_to(c, s[sp - 1], 0); 765 | break; 766 | case 0x05: 767 | if (sp < 2) 768 | return 0; 769 | for (; i + 1 < sp; i += 2) stbtt__csctx_rline_to(c, s[i], s[i + 1]); 770 | 771 | break; 772 | case 0x07: 773 | case 0x06: 774 | if (sp < 1) 775 | return 0; 776 | var goto_vlineto = b0 == 0x07 ? 1 : 0; 777 | for (; ; ) 778 | { 779 | if (goto_vlineto == 0) 780 | { 781 | if (i >= sp) 782 | break; 783 | stbtt__csctx_rline_to(c, s[i], 0); 784 | i++; 785 | } 786 | 787 | goto_vlineto = 0; 788 | if (i >= sp) 789 | break; 790 | stbtt__csctx_rline_to(c, 0, s[i]); 791 | i++; 792 | } 793 | 794 | break; 795 | case 0x1F: 796 | case 0x1E: 797 | if (sp < 4) 798 | return 0; 799 | var goto_hvcurveto = b0 == 0x1F ? 1 : 0; 800 | for (; ; ) 801 | { 802 | if (goto_hvcurveto == 0) 803 | { 804 | if (i + 3 >= sp) 805 | break; 806 | stbtt__csctx_rccurve_to(c, 0, s[i], s[i + 1], s[i + 2], s[i + 3], 807 | sp - i == 5 ? s[i + 4] : 0.0f); 808 | i += 4; 809 | } 810 | 811 | goto_hvcurveto = 0; 812 | if (i + 3 >= sp) 813 | break; 814 | stbtt__csctx_rccurve_to(c, s[i], 0, s[i + 1], s[i + 2], sp - i == 5 ? s[i + 4] : 0.0f, 815 | s[i + 3]); 816 | i += 4; 817 | } 818 | 819 | break; 820 | case 0x08: 821 | if (sp < 6) 822 | return 0; 823 | for (; i + 5 < sp; i += 6) 824 | stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5]); 825 | 826 | break; 827 | case 0x18: 828 | if (sp < 8) 829 | return 0; 830 | for (; i + 5 < sp - 2; i += 6) 831 | stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5]); 832 | 833 | if (i + 1 >= sp) 834 | return 0; 835 | stbtt__csctx_rline_to(c, s[i], s[i + 1]); 836 | break; 837 | case 0x19: 838 | if (sp < 8) 839 | return 0; 840 | for (; i + 1 < sp - 6; i += 2) stbtt__csctx_rline_to(c, s[i], s[i + 1]); 841 | 842 | if (i + 5 >= sp) 843 | return 0; 844 | stbtt__csctx_rccurve_to(c, s[i], s[i + 1], s[i + 2], s[i + 3], s[i + 4], s[i + 5]); 845 | break; 846 | case 0x1A: 847 | case 0x1B: 848 | if (sp < 4) 849 | return 0; 850 | f = (float)0.0; 851 | if ((sp & 1) != 0) 852 | { 853 | f = s[i]; 854 | i++; 855 | } 856 | 857 | for (; i + 3 < sp; i += 4) 858 | { 859 | if (b0 == 0x1B) 860 | stbtt__csctx_rccurve_to(c, s[i], f, s[i + 1], s[i + 2], s[i + 3], (float)0.0); 861 | else 862 | stbtt__csctx_rccurve_to(c, f, s[i], s[i + 1], s[i + 2], (float)0.0, s[i + 3]); 863 | f = (float)0.0; 864 | } 865 | 866 | break; 867 | case 0x0A: 868 | case 0x1D: 869 | if (b0 == 0x0A && has_subrs == 0) 870 | { 871 | if (info.fdselect.size != 0) 872 | subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); 873 | has_subrs = 1; 874 | } 875 | 876 | if (sp < 1) 877 | return 0; 878 | v = (int)s[--sp]; 879 | if (subr_stack_height >= 10) 880 | return 0; 881 | subr_stack[subr_stack_height++] = b; 882 | b = stbtt__get_subr(b0 == 0x0A ? subrs : info.gsubrs, v); 883 | if (b.size == 0) 884 | return 0; 885 | b.cursor = 0; 886 | clear_stack = 0; 887 | break; 888 | case 0x0B: 889 | if (subr_stack_height <= 0) 890 | return 0; 891 | b = subr_stack[--subr_stack_height]; 892 | clear_stack = 0; 893 | break; 894 | case 0x0E: 895 | stbtt__csctx_close_shape(c); 896 | return 1; 897 | case 0x0C: 898 | { 899 | float dx1 = 0; 900 | float dx2 = 0; 901 | float dx3 = 0; 902 | float dx4 = 0; 903 | float dx5 = 0; 904 | float dx6 = 0; 905 | float dy1 = 0; 906 | float dy2 = 0; 907 | float dy3 = 0; 908 | float dy4 = 0; 909 | float dy5 = 0; 910 | float dy6 = 0; 911 | float dx = 0; 912 | float dy = 0; 913 | int b1 = stbtt__buf_get8(&b); 914 | switch (b1) 915 | { 916 | case 0x22: 917 | if (sp < 7) 918 | return 0; 919 | dx1 = s[0]; 920 | dx2 = s[1]; 921 | dy2 = s[2]; 922 | dx3 = s[3]; 923 | dx4 = s[4]; 924 | dx5 = s[5]; 925 | dx6 = s[6]; 926 | stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); 927 | stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); 928 | break; 929 | case 0x23: 930 | if (sp < 13) 931 | return 0; 932 | dx1 = s[0]; 933 | dy1 = s[1]; 934 | dx2 = s[2]; 935 | dy2 = s[3]; 936 | dx3 = s[4]; 937 | dy3 = s[5]; 938 | dx4 = s[6]; 939 | dy4 = s[7]; 940 | dx5 = s[8]; 941 | dy5 = s[9]; 942 | dx6 = s[10]; 943 | dy6 = s[11]; 944 | stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); 945 | stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); 946 | break; 947 | case 0x24: 948 | if (sp < 9) 949 | return 0; 950 | dx1 = s[0]; 951 | dy1 = s[1]; 952 | dx2 = s[2]; 953 | dy2 = s[3]; 954 | dx3 = s[4]; 955 | dx4 = s[5]; 956 | dx5 = s[6]; 957 | dy5 = s[7]; 958 | dx6 = s[8]; 959 | stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); 960 | stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1 + dy2 + dy5)); 961 | break; 962 | case 0x25: 963 | if (sp < 11) 964 | return 0; 965 | dx1 = s[0]; 966 | dy1 = s[1]; 967 | dx2 = s[2]; 968 | dy2 = s[3]; 969 | dx3 = s[4]; 970 | dy3 = s[5]; 971 | dx4 = s[6]; 972 | dy4 = s[7]; 973 | dx5 = s[8]; 974 | dy5 = s[9]; 975 | dx6 = dy6 = s[10]; 976 | dx = dx1 + dx2 + dx3 + dx4 + dx5; 977 | dy = dy1 + dy2 + dy3 + dy4 + dy5; 978 | if (CRuntime.fabs(dx) > CRuntime.fabs(dy)) 979 | dy6 = -dy; 980 | else 981 | dx6 = -dx; 982 | stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); 983 | stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); 984 | break; 985 | default: 986 | return 0; 987 | } 988 | } 989 | 990 | break; 991 | default: 992 | if (b0 != 255 && b0 != 28 && b0 < 32) 993 | return 0; 994 | if (b0 == 255) 995 | { 996 | f = (float)(int)stbtt__buf_get(&b, 4) / 0x10000; 997 | } 998 | else 999 | { 1000 | stbtt__buf_skip(&b, -1); 1001 | f = (short)stbtt__cff_int(&b); 1002 | } 1003 | 1004 | if (sp >= 48) 1005 | return 0; 1006 | s[sp++] = f; 1007 | clear_stack = 0; 1008 | break; 1009 | } 1010 | 1011 | if (clear_stack != 0) 1012 | sp = 0; 1013 | } 1014 | 1015 | return 0; 1016 | } 1017 | 1018 | public static int stbtt_FindGlyphIndex(stbtt_fontinfo info, int unicode_codepoint) 1019 | { 1020 | var data = info.data; 1021 | var index_map = (uint)info.index_map; 1022 | var format = ttUSHORT(data + index_map + 0); 1023 | if (format == 0) 1024 | { 1025 | int bytes = ttUSHORT(data + index_map + 2); 1026 | if (unicode_codepoint < bytes - 6) 1027 | return *(data + index_map + 6 + unicode_codepoint); 1028 | return 0; 1029 | } 1030 | 1031 | if (format == 6) 1032 | { 1033 | uint first = ttUSHORT(data + index_map + 6); 1034 | uint count = ttUSHORT(data + index_map + 8); 1035 | if ((uint)unicode_codepoint >= first && (uint)unicode_codepoint < first + count) 1036 | return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first) * 2); 1037 | return 0; 1038 | } 1039 | 1040 | if (format == 2) return 0; 1041 | 1042 | if (format == 4) 1043 | { 1044 | var segcount = (ushort)(ttUSHORT(data + index_map + 6) >> 1); 1045 | var searchRange = (ushort)(ttUSHORT(data + index_map + 8) >> 1); 1046 | var entrySelector = ttUSHORT(data + index_map + 10); 1047 | var rangeShift = (ushort)(ttUSHORT(data + index_map + 12) >> 1); 1048 | var endCount = index_map + 14; 1049 | var search = endCount; 1050 | if (unicode_codepoint > 0xffff) 1051 | return 0; 1052 | if (unicode_codepoint >= ttUSHORT(data + search + rangeShift * 2)) 1053 | search += (uint)(rangeShift * 2); 1054 | search -= 2; 1055 | while (entrySelector != 0) 1056 | { 1057 | ushort end = 0; 1058 | searchRange >>= 1; 1059 | end = ttUSHORT(data + search + searchRange * 2); 1060 | if (unicode_codepoint > end) 1061 | search += (uint)(searchRange * 2); 1062 | --entrySelector; 1063 | } 1064 | 1065 | search += 2; 1066 | { 1067 | ushort offset = 0; 1068 | ushort start = 0; 1069 | ushort last = 0; 1070 | var item = (ushort)((search - endCount) >> 1); 1071 | start = ttUSHORT(data + index_map + 14 + segcount * 2 + 2 + 2 * item); 1072 | last = ttUSHORT(data + endCount + 2 * item); 1073 | if (unicode_codepoint < start || unicode_codepoint > last) 1074 | return 0; 1075 | offset = ttUSHORT(data + index_map + 14 + segcount * 6 + 2 + 2 * item); 1076 | if (offset == 0) 1077 | return (ushort)(unicode_codepoint + 1078 | ttSHORT(data + index_map + 14 + segcount * 4 + 2 + 2 * item)); 1079 | return ttUSHORT(data + offset + (unicode_codepoint - start) * 2 + index_map + 14 + segcount * 6 + 1080 | 2 + 2 * item); 1081 | } 1082 | } 1083 | 1084 | if (format == 12 || format == 13) 1085 | { 1086 | var ngroups = ttULONG(data + index_map + 12); 1087 | var low = 0; 1088 | var high = 0; 1089 | low = 0; 1090 | high = (int)ngroups; 1091 | while (low < high) 1092 | { 1093 | var mid = low + ((high - low) >> 1); 1094 | var start_char = ttULONG(data + index_map + 16 + mid * 12); 1095 | var end_char = ttULONG(data + index_map + 16 + mid * 12 + 4); 1096 | if ((uint)unicode_codepoint < start_char) 1097 | { 1098 | high = mid; 1099 | } 1100 | else if ((uint)unicode_codepoint > end_char) 1101 | { 1102 | low = mid + 1; 1103 | } 1104 | else 1105 | { 1106 | var start_glyph = ttULONG(data + index_map + 16 + mid * 12 + 8); 1107 | if (format == 12) 1108 | return (int)(start_glyph + unicode_codepoint - start_char); 1109 | return (int)start_glyph; 1110 | } 1111 | } 1112 | 1113 | return 0; 1114 | } 1115 | 1116 | return 0; 1117 | } 1118 | 1119 | public static byte* stbtt_FindSVGDoc(stbtt_fontinfo info, int gl) 1120 | { 1121 | var i = 0; 1122 | var data = info.data; 1123 | var svg_doc_list = data + stbtt__get_svg(info); 1124 | int numEntries = ttUSHORT(svg_doc_list); 1125 | var svg_docs = svg_doc_list + 2; 1126 | for (i = 0; i < numEntries; i++) 1127 | { 1128 | var svg_doc = svg_docs + 12 * i; 1129 | if (gl >= ttUSHORT(svg_doc) && gl <= ttUSHORT(svg_doc + 2)) 1130 | return svg_doc; 1131 | } 1132 | 1133 | return null; 1134 | } 1135 | 1136 | public static void stbtt_FreeShape(stbtt_fontinfo info, stbtt_vertex* v) 1137 | { 1138 | CRuntime.free(v); 1139 | } 1140 | 1141 | public static byte* stbtt_GetCodepointBitmap(stbtt_fontinfo info, float scale_x, float scale_y, int codepoint, 1142 | int* width, int* height, int* xoff, int* yoff) 1143 | { 1144 | return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, codepoint, width, height, xoff, 1145 | yoff); 1146 | } 1147 | 1148 | public static void stbtt_GetCodepointBitmapBox(stbtt_fontinfo font, int codepoint, float scale_x, float scale_y, 1149 | int* ix0, int* iy0, int* ix1, int* iy1) 1150 | { 1151 | stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y, 0.0f, 0.0f, ix0, iy0, ix1, iy1); 1152 | } 1153 | 1154 | public static void stbtt_GetCodepointBitmapBoxSubpixel(stbtt_fontinfo font, int codepoint, float scale_x, 1155 | float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1) 1156 | { 1157 | stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font, codepoint), scale_x, scale_y, shift_x, 1158 | shift_y, ix0, iy0, ix1, iy1); 1159 | } 1160 | 1161 | public static byte* stbtt_GetCodepointBitmapSubpixel(stbtt_fontinfo info, float scale_x, float scale_y, 1162 | float shift_x, float shift_y, int codepoint, int* width, int* height, int* xoff, int* yoff) 1163 | { 1164 | return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, shift_x, shift_y, 1165 | stbtt_FindGlyphIndex(info, codepoint), width, height, xoff, yoff); 1166 | } 1167 | 1168 | public static int stbtt_GetCodepointBox(stbtt_fontinfo info, int codepoint, int* x0, int* y0, int* x1, int* y1) 1169 | { 1170 | return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info, codepoint), x0, y0, x1, y1); 1171 | } 1172 | 1173 | public static void stbtt_GetCodepointHMetrics(stbtt_fontinfo info, int codepoint, int* advanceWidth, 1174 | int* leftSideBearing) 1175 | { 1176 | stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info, codepoint), advanceWidth, leftSideBearing); 1177 | } 1178 | 1179 | public static int stbtt_GetCodepointKernAdvance(stbtt_fontinfo info, int ch1, int ch2) 1180 | { 1181 | if (info.kern == 0 && info.gpos == 0) 1182 | return 0; 1183 | return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info, ch1), stbtt_FindGlyphIndex(info, ch2)); 1184 | } 1185 | 1186 | public static byte* stbtt_GetCodepointSDF(stbtt_fontinfo info, float scale, int codepoint, int padding, 1187 | byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff) 1188 | { 1189 | return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, 1190 | pixel_dist_scale, width, height, xoff, yoff); 1191 | } 1192 | 1193 | public static int stbtt_GetCodepointShape(stbtt_fontinfo info, int unicode_codepoint, stbtt_vertex** vertices) 1194 | { 1195 | return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); 1196 | } 1197 | 1198 | public static int stbtt_GetCodepointSVG(stbtt_fontinfo info, int unicode_codepoint, sbyte** svg) 1199 | { 1200 | return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); 1201 | } 1202 | 1203 | public static void stbtt_GetFontBoundingBox(stbtt_fontinfo info, int* x0, int* y0, int* x1, int* y1) 1204 | { 1205 | *x0 = ttSHORT(info.data + info.head + 36); 1206 | *y0 = ttSHORT(info.data + info.head + 38); 1207 | *x1 = ttSHORT(info.data + info.head + 40); 1208 | *y1 = ttSHORT(info.data + info.head + 42); 1209 | } 1210 | 1211 | public static sbyte* stbtt_GetFontNameString(stbtt_fontinfo font, int* length, int platformID, int encodingID, 1212 | int languageID, int nameID) 1213 | { 1214 | var i = 0; 1215 | var count = 0; 1216 | var stringOffset = 0; 1217 | var fc = font.data; 1218 | var offset = (uint)font.fontstart; 1219 | var nm = stbtt__find_table(fc, offset, "name"); 1220 | if (nm == 0) 1221 | return null; 1222 | count = ttUSHORT(fc + nm + 2); 1223 | stringOffset = (int)(nm + ttUSHORT(fc + nm + 4)); 1224 | for (i = 0; i < count; ++i) 1225 | { 1226 | var loc = (uint)(nm + 6 + 12 * i); 1227 | if (platformID == ttUSHORT(fc + loc + 0) && encodingID == ttUSHORT(fc + loc + 2) && 1228 | languageID == ttUSHORT(fc + loc + 4) && nameID == ttUSHORT(fc + loc + 6)) 1229 | { 1230 | *length = ttUSHORT(fc + loc + 8); 1231 | return (sbyte*)(fc + stringOffset + ttUSHORT(fc + loc + 10)); 1232 | } 1233 | } 1234 | 1235 | return null; 1236 | } 1237 | 1238 | public static void stbtt_GetFontVMetrics(stbtt_fontinfo info, int* ascent, int* descent, int* lineGap) 1239 | { 1240 | if (ascent != null) 1241 | *ascent = ttSHORT(info.data + info.hhea + 4); 1242 | if (descent != null) 1243 | *descent = ttSHORT(info.data + info.hhea + 6); 1244 | if (lineGap != null) 1245 | *lineGap = ttSHORT(info.data + info.hhea + 8); 1246 | } 1247 | 1248 | public static int stbtt_GetFontVMetricsOS2(stbtt_fontinfo info, int* typoAscent, int* typoDescent, 1249 | int* typoLineGap) 1250 | { 1251 | var tab = (int)stbtt__find_table(info.data, (uint)info.fontstart, "OS/2"); 1252 | if (tab == 0) 1253 | return 0; 1254 | if (typoAscent != null) 1255 | *typoAscent = ttSHORT(info.data + tab + 68); 1256 | if (typoDescent != null) 1257 | *typoDescent = ttSHORT(info.data + tab + 70); 1258 | if (typoLineGap != null) 1259 | *typoLineGap = ttSHORT(info.data + tab + 72); 1260 | return 1; 1261 | } 1262 | 1263 | public static byte* stbtt_GetGlyphBitmap(stbtt_fontinfo info, float scale_x, float scale_y, int glyph, 1264 | int* width, int* height, int* xoff, int* yoff) 1265 | { 1266 | return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); 1267 | } 1268 | 1269 | public static void stbtt_GetGlyphBitmapBox(stbtt_fontinfo font, int glyph, float scale_x, float scale_y, 1270 | int* ix0, int* iy0, int* ix1, int* iy1) 1271 | { 1272 | stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y, 0.0f, 0.0f, ix0, iy0, ix1, iy1); 1273 | } 1274 | 1275 | public static void stbtt_GetGlyphBitmapBoxSubpixel(stbtt_fontinfo font, int glyph, float scale_x, float scale_y, 1276 | float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1) 1277 | { 1278 | var x0 = 0; 1279 | var y0 = 0; 1280 | var x1 = 0; 1281 | var y1 = 0; 1282 | if (stbtt_GetGlyphBox(font, glyph, &x0, &y0, &x1, &y1) == 0) 1283 | { 1284 | if (ix0 != null) 1285 | *ix0 = 0; 1286 | if (iy0 != null) 1287 | *iy0 = 0; 1288 | if (ix1 != null) 1289 | *ix1 = 0; 1290 | if (iy1 != null) 1291 | *iy1 = 0; 1292 | } 1293 | else 1294 | { 1295 | if (ix0 != null) 1296 | *ix0 = (int)CRuntime.floor(x0 * scale_x + shift_x); 1297 | if (iy0 != null) 1298 | *iy0 = (int)CRuntime.floor(-y1 * scale_y + shift_y); 1299 | if (ix1 != null) 1300 | *ix1 = (int)CRuntime.ceil(x1 * scale_x + shift_x); 1301 | if (iy1 != null) 1302 | *iy1 = (int)CRuntime.ceil(-y0 * scale_y + shift_y); 1303 | } 1304 | } 1305 | 1306 | public static byte* stbtt_GetGlyphBitmapSubpixel(stbtt_fontinfo info, float scale_x, float scale_y, 1307 | float shift_x, float shift_y, int glyph, int* width, int* height, int* xoff, int* yoff) 1308 | { 1309 | var ix0 = 0; 1310 | var iy0 = 0; 1311 | var ix1 = 0; 1312 | var iy1 = 0; 1313 | var gbm = new stbtt__bitmap(); 1314 | stbtt_vertex* vertices; 1315 | var num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1316 | if (scale_x == 0) 1317 | scale_x = scale_y; 1318 | if (scale_y == 0) 1319 | { 1320 | if (scale_x == 0) 1321 | { 1322 | CRuntime.free(vertices); 1323 | return null; 1324 | } 1325 | 1326 | scale_y = scale_x; 1327 | } 1328 | 1329 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0, &iy0, &ix1, &iy1); 1330 | gbm.w = ix1 - ix0; 1331 | gbm.h = iy1 - iy0; 1332 | gbm.pixels = null; 1333 | if (width != null) 1334 | *width = gbm.w; 1335 | if (height != null) 1336 | *height = gbm.h; 1337 | if (xoff != null) 1338 | *xoff = ix0; 1339 | if (yoff != null) 1340 | *yoff = iy0; 1341 | if (gbm.w != 0 && gbm.h != 0) 1342 | { 1343 | gbm.pixels = (byte*)CRuntime.malloc((ulong)(gbm.w * gbm.h)); 1344 | if (gbm.pixels != null) 1345 | { 1346 | gbm.stride = gbm.w; 1347 | stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, 1348 | info.userdata, info.useOldRasterizer); 1349 | } 1350 | } 1351 | 1352 | CRuntime.free(vertices); 1353 | return gbm.pixels; 1354 | } 1355 | 1356 | public static int stbtt_GetGlyphBox(stbtt_fontinfo info, int glyph_index, int* x0, int* y0, int* x1, int* y1) 1357 | { 1358 | if (info.cff.size != 0) 1359 | { 1360 | stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); 1361 | } 1362 | else 1363 | { 1364 | var g = stbtt__GetGlyfOffset(info, glyph_index); 1365 | if (g < 0) 1366 | return 0; 1367 | if (x0 != null) 1368 | *x0 = ttSHORT(info.data + g + 2); 1369 | if (y0 != null) 1370 | *y0 = ttSHORT(info.data + g + 4); 1371 | if (x1 != null) 1372 | *x1 = ttSHORT(info.data + g + 6); 1373 | if (y1 != null) 1374 | *y1 = ttSHORT(info.data + g + 8); 1375 | } 1376 | 1377 | return 1; 1378 | } 1379 | 1380 | public static void stbtt_GetGlyphHMetrics(stbtt_fontinfo info, int glyph_index, int* advanceWidth, 1381 | int* leftSideBearing) 1382 | { 1383 | var numOfLongHorMetrics = ttUSHORT(info.data + info.hhea + 34); 1384 | if (glyph_index < numOfLongHorMetrics) 1385 | { 1386 | if (advanceWidth != null) 1387 | *advanceWidth = ttSHORT(info.data + info.hmtx + 4 * glyph_index); 1388 | if (leftSideBearing != null) 1389 | *leftSideBearing = ttSHORT(info.data + info.hmtx + 4 * glyph_index + 2); 1390 | } 1391 | else 1392 | { 1393 | if (advanceWidth != null) 1394 | *advanceWidth = ttSHORT(info.data + info.hmtx + 4 * (numOfLongHorMetrics - 1)); 1395 | if (leftSideBearing != null) 1396 | *leftSideBearing = ttSHORT(info.data + info.hmtx + 4 * numOfLongHorMetrics + 1397 | 2 * (glyph_index - numOfLongHorMetrics)); 1398 | } 1399 | } 1400 | 1401 | public static int stbtt_GetGlyphKernAdvance(stbtt_fontinfo info, int g1, int g2) 1402 | { 1403 | var xAdvance = 0; 1404 | if (info.gpos != 0) 1405 | xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); 1406 | else if (info.kern != 0) 1407 | xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); 1408 | return xAdvance; 1409 | } 1410 | 1411 | public static byte* stbtt_GetGlyphSDF(stbtt_fontinfo info, float scale, int glyph, int padding, 1412 | byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff) 1413 | { 1414 | var scale_x = scale; 1415 | var scale_y = scale; 1416 | var ix0 = 0; 1417 | var iy0 = 0; 1418 | var ix1 = 0; 1419 | var iy1 = 0; 1420 | var w = 0; 1421 | var h = 0; 1422 | byte* data; 1423 | if (scale == 0) 1424 | return null; 1425 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f, 0.0f, &ix0, &iy0, &ix1, &iy1); 1426 | if (ix0 == ix1 || iy0 == iy1) 1427 | return null; 1428 | ix0 -= padding; 1429 | iy0 -= padding; 1430 | ix1 += padding; 1431 | iy1 += padding; 1432 | w = ix1 - ix0; 1433 | h = iy1 - iy0; 1434 | if (width != null) 1435 | *width = w; 1436 | if (height != null) 1437 | *height = h; 1438 | if (xoff != null) 1439 | *xoff = ix0; 1440 | if (yoff != null) 1441 | *yoff = iy0; 1442 | scale_y = -scale_y; 1443 | { 1444 | var x = 0; 1445 | var y = 0; 1446 | var i = 0; 1447 | var j = 0; 1448 | float* precompute; 1449 | stbtt_vertex* verts; 1450 | var num_verts = stbtt_GetGlyphShape(info, glyph, &verts); 1451 | data = (byte*)CRuntime.malloc((ulong)(w * h)); 1452 | precompute = (float*)CRuntime.malloc((ulong)(num_verts * sizeof(float))); 1453 | for (i = 0, j = num_verts - 1; i < num_verts; j = i++) 1454 | if (verts[i].type == STBTT_vline) 1455 | { 1456 | var x0 = verts[i].x * scale_x; 1457 | var y0 = verts[i].y * scale_y; 1458 | var x1 = verts[j].x * scale_x; 1459 | var y1 = verts[j].y * scale_y; 1460 | var dist = (float)CRuntime.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); 1461 | precompute[i] = dist == 0 ? 0.0f : 1.0f / dist; 1462 | } 1463 | else if (verts[i].type == STBTT_vcurve) 1464 | { 1465 | var x2 = verts[j].x * scale_x; 1466 | var y2 = verts[j].y * scale_y; 1467 | var x1 = verts[i].cx * scale_x; 1468 | var y1 = verts[i].cy * scale_y; 1469 | var x0 = verts[i].x * scale_x; 1470 | var y0 = verts[i].y * scale_y; 1471 | var bx = x0 - 2 * x1 + x2; 1472 | var by = y0 - 2 * y1 + y2; 1473 | var len2 = bx * bx + by * by; 1474 | if (len2 != 0.0f) 1475 | precompute[i] = 1.0f / (bx * bx + by * by); 1476 | else 1477 | precompute[i] = 0.0f; 1478 | } 1479 | else 1480 | { 1481 | precompute[i] = 0.0f; 1482 | } 1483 | 1484 | for (y = iy0; y < iy1; ++y) 1485 | for (x = ix0; x < ix1; ++x) 1486 | { 1487 | float val = 0; 1488 | var min_dist = 999999.0f; 1489 | var sx = x + 0.5f; 1490 | var sy = y + 0.5f; 1491 | var x_gspace = sx / scale_x; 1492 | var y_gspace = sy / scale_y; 1493 | var winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); 1494 | for (i = 0; i < num_verts; ++i) 1495 | { 1496 | var x0 = verts[i].x * scale_x; 1497 | var y0 = verts[i].y * scale_y; 1498 | if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) 1499 | { 1500 | var x1 = verts[i - 1].x * scale_x; 1501 | var y1 = verts[i - 1].y * scale_y; 1502 | float dist = 0; 1503 | var dist2 = (x0 - sx) * (x0 - sx) + (y0 - sy) * (y0 - sy); 1504 | if (dist2 < min_dist * min_dist) 1505 | min_dist = (float)CRuntime.sqrt(dist2); 1506 | dist = CRuntime.fabs((x1 - x0) * (y0 - sy) - (y1 - y0) * (x0 - sx)) * precompute[i]; 1507 | if (dist < min_dist) 1508 | { 1509 | var dx = x1 - x0; 1510 | var dy = y1 - y0; 1511 | var px = x0 - sx; 1512 | var py = y0 - sy; 1513 | var t = -(px * dx + py * dy) / (dx * dx + dy * dy); 1514 | if (t >= 0.0f && t <= 1.0f) 1515 | min_dist = dist; 1516 | } 1517 | } 1518 | else if (verts[i].type == STBTT_vcurve) 1519 | { 1520 | var x2 = verts[i - 1].x * scale_x; 1521 | var y2 = verts[i - 1].y * scale_y; 1522 | var x1 = verts[i].cx * scale_x; 1523 | var y1 = verts[i].cy * scale_y; 1524 | var box_x0 = (x0 < x1 ? x0 : x1) < x2 ? x0 < x1 ? x0 : x1 : x2; 1525 | var box_y0 = (y0 < y1 ? y0 : y1) < y2 ? y0 < y1 ? y0 : y1 : y2; 1526 | var box_x1 = (x0 < x1 ? x1 : x0) < x2 ? x2 : x0 < x1 ? x1 : x0; 1527 | var box_y1 = (y0 < y1 ? y1 : y0) < y2 ? y2 : y0 < y1 ? y1 : y0; 1528 | if (sx > box_x0 - min_dist && sx < box_x1 + min_dist && sy > box_y0 - min_dist && 1529 | sy < box_y1 + min_dist) 1530 | { 1531 | var num = 0; 1532 | var ax = x1 - x0; 1533 | var ay = y1 - y0; 1534 | var bx = x0 - 2 * x1 + x2; 1535 | var by = y0 - 2 * y1 + y2; 1536 | var mx = x0 - sx; 1537 | var my = y0 - sy; 1538 | var res = stackalloc float[] { 0, 0, 0 }; 1539 | float px = 0; 1540 | float py = 0; 1541 | float t = 0; 1542 | float it = 0; 1543 | float dist2 = 0; 1544 | var a_inv = precompute[i]; 1545 | if (a_inv == 0.0) 1546 | { 1547 | var a = 3 * (ax * bx + ay * by); 1548 | var b = 2 * (ax * ax + ay * ay) + (mx * bx + my * by); 1549 | var c = mx * ax + my * ay; 1550 | if (a == 0.0) 1551 | { 1552 | if (b != 0.0) res[num++] = -c / b; 1553 | } 1554 | else 1555 | { 1556 | var discriminant = b * b - 4 * a * c; 1557 | if (discriminant < 0) 1558 | { 1559 | num = 0; 1560 | } 1561 | else 1562 | { 1563 | var root = (float)CRuntime.sqrt(discriminant); 1564 | res[0] = (-b - root) / (2 * a); 1565 | res[1] = (-b + root) / (2 * a); 1566 | num = 2; 1567 | } 1568 | } 1569 | } 1570 | else 1571 | { 1572 | var b = 3 * (ax * bx + ay * by) * a_inv; 1573 | var c = (2 * (ax * ax + ay * ay) + (mx * bx + my * by)) * a_inv; 1574 | var d = (mx * ax + my * ay) * a_inv; 1575 | num = stbtt__solve_cubic(b, c, d, res); 1576 | } 1577 | 1578 | dist2 = (x0 - sx) * (x0 - sx) + (y0 - sy) * (y0 - sy); 1579 | if (dist2 < min_dist * min_dist) 1580 | min_dist = (float)CRuntime.sqrt(dist2); 1581 | if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) 1582 | { 1583 | t = res[0]; 1584 | it = 1.0f - t; 1585 | px = it * it * x0 + 2 * t * it * x1 + t * t * x2; 1586 | py = it * it * y0 + 2 * t * it * y1 + t * t * y2; 1587 | dist2 = (px - sx) * (px - sx) + (py - sy) * (py - sy); 1588 | if (dist2 < min_dist * min_dist) 1589 | min_dist = (float)CRuntime.sqrt(dist2); 1590 | } 1591 | 1592 | if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) 1593 | { 1594 | t = res[1]; 1595 | it = 1.0f - t; 1596 | px = it * it * x0 + 2 * t * it * x1 + t * t * x2; 1597 | py = it * it * y0 + 2 * t * it * y1 + t * t * y2; 1598 | dist2 = (px - sx) * (px - sx) + (py - sy) * (py - sy); 1599 | if (dist2 < min_dist * min_dist) 1600 | min_dist = (float)CRuntime.sqrt(dist2); 1601 | } 1602 | 1603 | if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) 1604 | { 1605 | t = res[2]; 1606 | it = 1.0f - t; 1607 | px = it * it * x0 + 2 * t * it * x1 + t * t * x2; 1608 | py = it * it * y0 + 2 * t * it * y1 + t * t * y2; 1609 | dist2 = (px - sx) * (px - sx) + (py - sy) * (py - sy); 1610 | if (dist2 < min_dist * min_dist) 1611 | min_dist = (float)CRuntime.sqrt(dist2); 1612 | } 1613 | } 1614 | } 1615 | } 1616 | 1617 | if (winding == 0) 1618 | min_dist = -min_dist; 1619 | val = onedge_value + pixel_dist_scale * min_dist; 1620 | if (val < 0) 1621 | val = 0; 1622 | else if (val > 255) 1623 | val = 255; 1624 | data[(y - iy0) * w + (x - ix0)] = (byte)val; 1625 | } 1626 | 1627 | CRuntime.free(precompute); 1628 | CRuntime.free(verts); 1629 | } 1630 | 1631 | return data; 1632 | } 1633 | 1634 | public static int stbtt_GetGlyphShape(stbtt_fontinfo info, int glyph_index, stbtt_vertex** pvertices) 1635 | { 1636 | if (info.cff.size == 0) 1637 | return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); 1638 | return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); 1639 | } 1640 | 1641 | public static int stbtt_GetGlyphSVG(stbtt_fontinfo info, int gl, sbyte** svg) 1642 | { 1643 | var data = info.data; 1644 | byte* svg_doc; 1645 | if (info.svg == 0) 1646 | return 0; 1647 | svg_doc = stbtt_FindSVGDoc(info, gl); 1648 | if (svg_doc != null) 1649 | { 1650 | *svg = (sbyte*)data + info.svg + ttULONG(svg_doc + 4); 1651 | return (int)ttULONG(svg_doc + 8); 1652 | } 1653 | 1654 | return 0; 1655 | } 1656 | 1657 | public static int stbtt_GetKerningTable(stbtt_fontinfo info, stbtt_kerningentry* table, int table_length) 1658 | { 1659 | var data = info.data + info.kern; 1660 | var k = 0; 1661 | var length = 0; 1662 | if (info.kern == 0) 1663 | return 0; 1664 | if (ttUSHORT(data + 2) < 1) 1665 | return 0; 1666 | if (ttUSHORT(data + 8) != 1) 1667 | return 0; 1668 | length = ttUSHORT(data + 10); 1669 | if (table_length < length) 1670 | length = table_length; 1671 | for (k = 0; k < length; k++) 1672 | { 1673 | table[k].glyph1 = ttUSHORT(data + 18 + k * 6); 1674 | table[k].glyph2 = ttUSHORT(data + 20 + k * 6); 1675 | table[k].advance = ttSHORT(data + 22 + k * 6); 1676 | } 1677 | 1678 | return length; 1679 | } 1680 | 1681 | public static int stbtt_GetKerningTableLength(stbtt_fontinfo info) 1682 | { 1683 | var data = info.data + info.kern; 1684 | if (info.kern == 0) 1685 | return 0; 1686 | if (ttUSHORT(data + 2) < 1) 1687 | return 0; 1688 | if (ttUSHORT(data + 8) != 1) 1689 | return 0; 1690 | return ttUSHORT(data + 10); 1691 | } 1692 | 1693 | public static int stbtt_InitFont(stbtt_fontinfo info, byte* data, int offset) 1694 | { 1695 | return stbtt_InitFont_internal(info, data, offset); 1696 | } 1697 | 1698 | public static int stbtt_InitFont_internal(stbtt_fontinfo info, byte* data, int fontstart) 1699 | { 1700 | uint cmap = 0; 1701 | uint t = 0; 1702 | var i = 0; 1703 | var numTables = 0; 1704 | info.data = data; 1705 | info.fontstart = fontstart; 1706 | info.cff = stbtt__new_buf(null, 0); 1707 | cmap = stbtt__find_table(data, (uint)fontstart, "cmap"); 1708 | info.loca = (int)stbtt__find_table(data, (uint)fontstart, "loca"); 1709 | info.head = (int)stbtt__find_table(data, (uint)fontstart, "head"); 1710 | info.glyf = (int)stbtt__find_table(data, (uint)fontstart, "glyf"); 1711 | info.hhea = (int)stbtt__find_table(data, (uint)fontstart, "hhea"); 1712 | info.hmtx = (int)stbtt__find_table(data, (uint)fontstart, "hmtx"); 1713 | info.kern = (int)stbtt__find_table(data, (uint)fontstart, "kern"); 1714 | info.gpos = (int)stbtt__find_table(data, (uint)fontstart, "GPOS"); 1715 | if (cmap == 0 || info.head == 0 || info.hhea == 0 || info.hmtx == 0) 1716 | return 0; 1717 | if (info.glyf != 0) 1718 | { 1719 | if (info.loca == 0) 1720 | return 0; 1721 | } 1722 | else 1723 | { 1724 | var b = new stbtt__buf(); 1725 | var topdict = new stbtt__buf(); 1726 | var topdictidx = new stbtt__buf(); 1727 | uint cstype = 2; 1728 | uint charstrings = 0; 1729 | uint fdarrayoff = 0; 1730 | uint fdselectoff = 0; 1731 | uint cff = 0; 1732 | cff = stbtt__find_table(data, (uint)fontstart, "CFF "); 1733 | if (cff == 0) 1734 | return 0; 1735 | info.fontdicts = stbtt__new_buf(null, 0); 1736 | info.fdselect = stbtt__new_buf(null, 0); 1737 | info.cff = stbtt__new_buf(data + cff, 512 * 1024 * 1024); 1738 | b = info.cff; 1739 | stbtt__buf_skip(&b, 2); 1740 | stbtt__buf_seek(&b, stbtt__buf_get8(&b)); 1741 | stbtt__cff_get_index(&b); 1742 | topdictidx = stbtt__cff_get_index(&b); 1743 | topdict = stbtt__cff_index_get(topdictidx, 0); 1744 | stbtt__cff_get_index(&b); 1745 | info.gsubrs = stbtt__cff_get_index(&b); 1746 | stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); 1747 | stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); 1748 | stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); 1749 | stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); 1750 | info.subrs = stbtt__get_subrs(b, topdict); 1751 | if (cstype != 2) 1752 | return 0; 1753 | if (charstrings == 0) 1754 | return 0; 1755 | if (fdarrayoff != 0) 1756 | { 1757 | if (fdselectoff == 0) 1758 | return 0; 1759 | stbtt__buf_seek(&b, (int)fdarrayoff); 1760 | info.fontdicts = stbtt__cff_get_index(&b); 1761 | info.fdselect = stbtt__buf_range(&b, (int)fdselectoff, (int)(b.size - fdselectoff)); 1762 | } 1763 | 1764 | stbtt__buf_seek(&b, (int)charstrings); 1765 | info.charstrings = stbtt__cff_get_index(&b); 1766 | } 1767 | 1768 | t = stbtt__find_table(data, (uint)fontstart, "maxp"); 1769 | if (t != 0) 1770 | info.numGlyphs = ttUSHORT(data + t + 4); 1771 | else 1772 | info.numGlyphs = 0xffff; 1773 | info.svg = -1; 1774 | numTables = ttUSHORT(data + cmap + 2); 1775 | info.index_map = 0; 1776 | for (i = 0; i < numTables; ++i) 1777 | { 1778 | var encoding_record = (uint)(cmap + 4 + 8 * i); 1779 | switch (ttUSHORT(data + encoding_record)) 1780 | { 1781 | case STBTT_PLATFORM_ID_MICROSOFT: 1782 | switch (ttUSHORT(data + encoding_record + 2)) 1783 | { 1784 | case STBTT_MS_EID_UNICODE_BMP: 1785 | case STBTT_MS_EID_UNICODE_FULL: 1786 | info.index_map = (int)(cmap + ttULONG(data + encoding_record + 4)); 1787 | break; 1788 | } 1789 | 1790 | break; 1791 | case STBTT_PLATFORM_ID_UNICODE: 1792 | info.index_map = (int)(cmap + ttULONG(data + encoding_record + 4)); 1793 | break; 1794 | } 1795 | } 1796 | 1797 | if (info.index_map == 0) 1798 | throw new Exception("The font does not have a table mapping from unicode codepoints to font indices."); 1799 | info.indexToLocFormat = ttUSHORT(data + info.head + 50); 1800 | return 1; 1801 | } 1802 | 1803 | public static int stbtt_IsGlyphEmpty(stbtt_fontinfo info, int glyph_index) 1804 | { 1805 | short numberOfContours = 0; 1806 | var g = 0; 1807 | if (info.cff.size != 0) 1808 | return stbtt__GetGlyphInfoT2(info, glyph_index, null, null, null, null) == 0 ? 1 : 0; 1809 | g = stbtt__GetGlyfOffset(info, glyph_index); 1810 | if (g < 0) 1811 | return 1; 1812 | numberOfContours = ttSHORT(info.data + g); 1813 | return numberOfContours == 0 ? 1 : 0; 1814 | } 1815 | 1816 | public static void stbtt_MakeCodepointBitmap(stbtt_fontinfo info, byte* output, int out_w, int out_h, 1817 | int out_stride, float scale_x, float scale_y, int codepoint) 1818 | { 1819 | stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f, 0.0f, 1820 | codepoint); 1821 | } 1822 | 1823 | public static void stbtt_MakeCodepointBitmapSubpixel(stbtt_fontinfo info, byte* output, int out_w, int out_h, 1824 | int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) 1825 | { 1826 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, 1827 | stbtt_FindGlyphIndex(info, codepoint)); 1828 | } 1829 | 1830 | public static void stbtt_MakeCodepointBitmapSubpixelPrefilter(stbtt_fontinfo info, byte* output, int out_w, 1831 | int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, 1832 | int oversample_y, float* sub_x, float* sub_y, int codepoint) 1833 | { 1834 | stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, 1835 | shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info, codepoint)); 1836 | } 1837 | 1838 | public static void stbtt_MakeGlyphBitmap(stbtt_fontinfo info, byte* output, int out_w, int out_h, 1839 | int out_stride, float scale_x, float scale_y, int glyph) 1840 | { 1841 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f, 0.0f, glyph); 1842 | } 1843 | 1844 | public static void stbtt_MakeGlyphBitmapSubpixel(stbtt_fontinfo info, byte* output, int out_w, int out_h, 1845 | int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) 1846 | { 1847 | var ix0 = 0; 1848 | var iy0 = 0; 1849 | stbtt_vertex* vertices; 1850 | var num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1851 | var gbm = new stbtt__bitmap(); 1852 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0, &iy0, null, null); 1853 | gbm.pixels = output; 1854 | gbm.w = out_w; 1855 | gbm.h = out_h; 1856 | gbm.stride = out_stride; 1857 | if (gbm.w != 0 && gbm.h != 0) 1858 | stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, 1859 | info.userdata, info.useOldRasterizer); 1860 | CRuntime.free(vertices); 1861 | } 1862 | 1863 | public static void stbtt_MakeGlyphBitmapSubpixelPrefilter(stbtt_fontinfo info, byte* output, int out_w, 1864 | int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, 1865 | int prefilter_y, float* sub_x, float* sub_y, int glyph) 1866 | { 1867 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w - (prefilter_x - 1), out_h - (prefilter_y - 1), 1868 | out_stride, scale_x, scale_y, shift_x, shift_y, glyph); 1869 | if (prefilter_x > 1) 1870 | stbtt__h_prefilter(output, out_w, out_h, out_stride, (uint)prefilter_x); 1871 | if (prefilter_y > 1) 1872 | stbtt__v_prefilter(output, out_w, out_h, out_stride, (uint)prefilter_y); 1873 | *sub_x = stbtt__oversample_shift(prefilter_x); 1874 | *sub_y = stbtt__oversample_shift(prefilter_y); 1875 | } 1876 | 1877 | public static float stbtt_ScaleForMappingEmToPixels(stbtt_fontinfo info, float pixels) 1878 | { 1879 | int unitsPerEm = ttUSHORT(info.data + info.head + 18); 1880 | return pixels / unitsPerEm; 1881 | } 1882 | 1883 | public static float stbtt_ScaleForPixelHeight(stbtt_fontinfo info, float height) 1884 | { 1885 | var fheight = ttSHORT(info.data + info.hhea + 4) - ttSHORT(info.data + info.hhea + 6); 1886 | return height / fheight; 1887 | } 1888 | 1889 | [StructLayout(LayoutKind.Sequential)] 1890 | public struct stbtt_kerningentry 1891 | { 1892 | public int glyph1; 1893 | public int glyph2; 1894 | public int advance; 1895 | } 1896 | } 1897 | } -------------------------------------------------------------------------------- /src/StbTrueType.Generated.Heap.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using Hebron.Runtime; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace StbTrueTypeSharp 7 | { 8 | unsafe partial class StbTrueType 9 | { 10 | public static void* stbtt__hheap_alloc(stbtt__hheap* hh, ulong size, void* userdata) 11 | { 12 | if (hh->first_free != null) 13 | { 14 | var p = hh->first_free; 15 | hh->first_free = *(void**)p; 16 | return p; 17 | } 18 | 19 | if (hh->num_remaining_in_head_chunk == 0) 20 | { 21 | var count = size < 32 ? 2000 : size < 128 ? 800 : 100; 22 | var c = (stbtt__hheap_chunk*)CRuntime.malloc((ulong)sizeof(stbtt__hheap_chunk) + 23 | size * (ulong)count); 24 | if (c == null) 25 | return null; 26 | c->next = hh->head; 27 | hh->head = c; 28 | hh->num_remaining_in_head_chunk = count; 29 | } 30 | 31 | --hh->num_remaining_in_head_chunk; 32 | return (sbyte*)hh->head + sizeof(stbtt__hheap_chunk) + size * (ulong)hh->num_remaining_in_head_chunk; 33 | } 34 | 35 | public static void stbtt__hheap_cleanup(stbtt__hheap* hh, void* userdata) 36 | { 37 | var c = hh->head; 38 | while (c != null) 39 | { 40 | var n = c->next; 41 | CRuntime.free(c); 42 | c = n; 43 | } 44 | } 45 | 46 | public static void stbtt__hheap_free(stbtt__hheap* hh, void* p) 47 | { 48 | *(void**)p = hh->first_free; 49 | hh->first_free = p; 50 | } 51 | 52 | public static stbtt__active_edge* stbtt__new_active(stbtt__hheap* hh, stbtt__edge* e, int off_x, 53 | float start_point, void* userdata) 54 | { 55 | var z = (stbtt__active_edge*)stbtt__hheap_alloc(hh, (ulong)sizeof(stbtt__active_edge), userdata); 56 | var dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 57 | if (z == null) 58 | return z; 59 | z->fdx = dxdy; 60 | z->fdy = dxdy != 0.0f ? 1.0f / dxdy : 0.0f; 61 | z->fx = e->x0 + dxdy * (start_point - e->y0); 62 | z->fx -= off_x; 63 | z->direction = e->invert != 0 ? 1.0f : -1.0f; 64 | z->sy = e->y0; 65 | z->ey = e->y1; 66 | z->next = null; 67 | return z; 68 | } 69 | 70 | [StructLayout(LayoutKind.Sequential)] 71 | public struct stbtt__hheap 72 | { 73 | public stbtt__hheap_chunk* head; 74 | public void* first_free; 75 | public int num_remaining_in_head_chunk; 76 | } 77 | 78 | [StructLayout(LayoutKind.Sequential)] 79 | public struct stbtt__hheap_chunk 80 | { 81 | public stbtt__hheap_chunk* next; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/StbTrueType.Generated.RectPack.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 1/2/2022 4:23:36 AM 2 | 3 | using Hebron.Runtime; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace StbTrueTypeSharp 7 | { 8 | unsafe partial class StbTrueType 9 | { 10 | public static void stbrp_init_target(stbrp_context* con, int pw, int ph, stbrp_node* nodes, int num_nodes) 11 | { 12 | con->width = pw; 13 | con->height = ph; 14 | con->x = 0; 15 | con->y = 0; 16 | con->bottom_y = 0; 17 | } 18 | 19 | public static void stbrp_pack_rects(stbrp_context* con, stbrp_rect* rects, int num_rects) 20 | { 21 | var i = 0; 22 | for (i = 0; i < num_rects; ++i) 23 | { 24 | if (con->x + rects[i].w > con->width) 25 | { 26 | con->x = 0; 27 | con->y = con->bottom_y; 28 | } 29 | 30 | if (con->y + rects[i].h > con->height) 31 | break; 32 | rects[i].x = con->x; 33 | rects[i].y = con->y; 34 | rects[i].was_packed = 1; 35 | con->x += rects[i].w; 36 | if (con->y + rects[i].h > con->bottom_y) 37 | con->bottom_y = con->y + rects[i].h; 38 | } 39 | 40 | for (; i < num_rects; ++i) rects[i].was_packed = 0; 41 | } 42 | 43 | public static int stbtt_PackBegin(stbtt_pack_context spc, byte* pixels, int pw, int ph, int stride_in_bytes, 44 | int padding, void* alloc_context) 45 | { 46 | var context = (stbrp_context*)CRuntime.malloc((ulong)sizeof(stbrp_context)); 47 | var num_nodes = pw - padding; 48 | var nodes = (stbrp_node*)CRuntime.malloc((ulong)(sizeof(stbrp_node) * num_nodes)); 49 | if (context == null || nodes == null) 50 | { 51 | if (context != null) 52 | CRuntime.free(context); 53 | if (nodes != null) 54 | CRuntime.free(nodes); 55 | return 0; 56 | } 57 | 58 | spc.user_allocator_context = alloc_context; 59 | spc.width = pw; 60 | spc.height = ph; 61 | spc.pixels = pixels; 62 | spc.pack_info = context; 63 | spc.nodes = nodes; 64 | spc.padding = padding; 65 | spc.stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 66 | spc.h_oversample = 1; 67 | spc.v_oversample = 1; 68 | spc.skip_missing = 0; 69 | stbrp_init_target(context, pw - padding, ph - padding, nodes, num_nodes); 70 | if (pixels != null) 71 | CRuntime.memset(pixels, 0, (ulong)(pw * ph)); 72 | return 1; 73 | } 74 | 75 | public static void stbtt_PackEnd(stbtt_pack_context spc) 76 | { 77 | CRuntime.free(spc.nodes); 78 | CRuntime.free(spc.pack_info); 79 | } 80 | 81 | public static int stbtt_PackFontRange(stbtt_pack_context spc, byte* fontdata, int font_index, float font_size, 82 | int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar* chardata_for_range) 83 | { 84 | var range = new stbtt_pack_range(); 85 | range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; 86 | range.array_of_unicode_codepoints = null; 87 | range.num_chars = num_chars_in_range; 88 | range.chardata_for_range = chardata_for_range; 89 | range.font_size = font_size; 90 | return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 91 | } 92 | 93 | public static int stbtt_PackFontRanges(stbtt_pack_context spc, byte* fontdata, int font_index, 94 | stbtt_pack_range* ranges, int num_ranges) 95 | { 96 | var info = new stbtt_fontinfo(); 97 | var i = 0; 98 | var j = 0; 99 | var n = 0; 100 | var return_value = 1; 101 | stbrp_rect* rects; 102 | for (i = 0; i < num_ranges; ++i) 103 | for (j = 0; j < ranges[i].num_chars; ++j) 104 | ranges[i].chardata_for_range[j].x0 = ranges[i].chardata_for_range[j].y0 = 105 | ranges[i].chardata_for_range[j].x1 = ranges[i].chardata_for_range[j].y1 = 0; 106 | 107 | n = 0; 108 | for (i = 0; i < num_ranges; ++i) n += ranges[i].num_chars; 109 | 110 | rects = (stbrp_rect*)CRuntime.malloc((ulong)(sizeof(stbrp_rect) * n)); 111 | if (rects == null) 112 | return 0; 113 | info.userdata = spc.user_allocator_context; 114 | stbtt_InitFont(info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, font_index)); 115 | n = stbtt_PackFontRangesGatherRects(spc, info, ranges, num_ranges, rects); 116 | stbtt_PackFontRangesPackRects(spc, rects, n); 117 | return_value = stbtt_PackFontRangesRenderIntoRects(spc, info, ranges, num_ranges, rects); 118 | CRuntime.free(rects); 119 | return return_value; 120 | } 121 | 122 | public static int stbtt_PackFontRangesGatherRects(stbtt_pack_context spc, stbtt_fontinfo info, 123 | stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects) 124 | { 125 | var i = 0; 126 | var j = 0; 127 | var k = 0; 128 | var missing_glyph_added = 0; 129 | k = 0; 130 | for (i = 0; i < num_ranges; ++i) 131 | { 132 | var fh = ranges[i].font_size; 133 | var scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); 134 | ranges[i].h_oversample = (byte)spc.h_oversample; 135 | ranges[i].v_oversample = (byte)spc.v_oversample; 136 | for (j = 0; j < ranges[i].num_chars; ++j) 137 | { 138 | var x0 = 0; 139 | var y0 = 0; 140 | var x1 = 0; 141 | var y1 = 0; 142 | var codepoint = ranges[i].array_of_unicode_codepoints == null 143 | ? ranges[i].first_unicode_codepoint_in_range + j 144 | : ranges[i].array_of_unicode_codepoints[j]; 145 | var glyph = stbtt_FindGlyphIndex(info, codepoint); 146 | if (glyph == 0 && (spc.skip_missing != 0 || missing_glyph_added != 0)) 147 | { 148 | rects[k].w = rects[k].h = 0; 149 | } 150 | else 151 | { 152 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale * spc.h_oversample, scale * spc.v_oversample, 153 | 0, 0, &x0, &y0, &x1, &y1); 154 | rects[k].w = (int)(x1 - x0 + spc.padding + spc.h_oversample - 1); 155 | rects[k].h = (int)(y1 - y0 + spc.padding + spc.v_oversample - 1); 156 | if (glyph == 0) 157 | missing_glyph_added = 1; 158 | } 159 | 160 | ++k; 161 | } 162 | } 163 | 164 | return k; 165 | } 166 | 167 | public static void stbtt_PackFontRangesPackRects(stbtt_pack_context spc, stbrp_rect* rects, int num_rects) 168 | { 169 | stbrp_pack_rects((stbrp_context*)spc.pack_info, rects, num_rects); 170 | } 171 | 172 | public static int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context spc, stbtt_fontinfo info, 173 | stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects) 174 | { 175 | var i = 0; 176 | var j = 0; 177 | var k = 0; 178 | var missing_glyph = -1; 179 | var return_value = 1; 180 | var old_h_over = (int)spc.h_oversample; 181 | var old_v_over = (int)spc.v_oversample; 182 | k = 0; 183 | for (i = 0; i < num_ranges; ++i) 184 | { 185 | var fh = ranges[i].font_size; 186 | var scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); 187 | float recip_h = 0; 188 | float recip_v = 0; 189 | float sub_x = 0; 190 | float sub_y = 0; 191 | spc.h_oversample = ranges[i].h_oversample; 192 | spc.v_oversample = ranges[i].v_oversample; 193 | recip_h = 1.0f / spc.h_oversample; 194 | recip_v = 1.0f / spc.v_oversample; 195 | sub_x = stbtt__oversample_shift((int)spc.h_oversample); 196 | sub_y = stbtt__oversample_shift((int)spc.v_oversample); 197 | for (j = 0; j < ranges[i].num_chars; ++j) 198 | { 199 | var r = &rects[k]; 200 | if (r->was_packed != 0 && r->w != 0 && r->h != 0) 201 | { 202 | var bc = &ranges[i].chardata_for_range[j]; 203 | var advance = 0; 204 | var lsb = 0; 205 | var x0 = 0; 206 | var y0 = 0; 207 | var x1 = 0; 208 | var y1 = 0; 209 | var codepoint = ranges[i].array_of_unicode_codepoints == null 210 | ? ranges[i].first_unicode_codepoint_in_range + j 211 | : ranges[i].array_of_unicode_codepoints[j]; 212 | var glyph = stbtt_FindGlyphIndex(info, codepoint); 213 | var pad = spc.padding; 214 | r->x += pad; 215 | r->y += pad; 216 | r->w -= pad; 217 | r->h -= pad; 218 | stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); 219 | stbtt_GetGlyphBitmapBox(info, glyph, scale * spc.h_oversample, scale * spc.v_oversample, &x0, 220 | &y0, &x1, &y1); 221 | stbtt_MakeGlyphBitmapSubpixel(info, spc.pixels + r->x + r->y * spc.stride_in_bytes, 222 | (int)(r->w - spc.h_oversample + 1), (int)(r->h - spc.v_oversample + 1), 223 | spc.stride_in_bytes, scale * spc.h_oversample, scale * spc.v_oversample, 0, 0, glyph); 224 | if (spc.h_oversample > 1) 225 | stbtt__h_prefilter(spc.pixels + r->x + r->y * spc.stride_in_bytes, r->w, r->h, 226 | spc.stride_in_bytes, spc.h_oversample); 227 | if (spc.v_oversample > 1) 228 | stbtt__v_prefilter(spc.pixels + r->x + r->y * spc.stride_in_bytes, r->w, r->h, 229 | spc.stride_in_bytes, spc.v_oversample); 230 | bc->x0 = (ushort)(short)r->x; 231 | bc->y0 = (ushort)(short)r->y; 232 | bc->x1 = (ushort)(short)(r->x + r->w); 233 | bc->y1 = (ushort)(short)(r->y + r->h); 234 | bc->xadvance = scale * advance; 235 | bc->xoff = x0 * recip_h + sub_x; 236 | bc->yoff = y0 * recip_v + sub_y; 237 | bc->xoff2 = (x0 + r->w) * recip_h + sub_x; 238 | bc->yoff2 = (y0 + r->h) * recip_v + sub_y; 239 | if (glyph == 0) 240 | missing_glyph = j; 241 | } 242 | else if (spc.skip_missing != 0) 243 | { 244 | return_value = 0; 245 | } 246 | else if (r->was_packed != 0 && r->w == 0 && r->h == 0 && missing_glyph >= 0) 247 | { 248 | ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; 249 | } 250 | else 251 | { 252 | return_value = 0; 253 | } 254 | 255 | ++k; 256 | } 257 | } 258 | 259 | spc.h_oversample = (uint)old_h_over; 260 | spc.v_oversample = (uint)old_v_over; 261 | return return_value; 262 | } 263 | 264 | public static void stbtt_PackSetOversampling(stbtt_pack_context spc, uint h_oversample, uint v_oversample) 265 | { 266 | if (h_oversample <= 8) 267 | spc.h_oversample = h_oversample; 268 | if (v_oversample <= 8) 269 | spc.v_oversample = v_oversample; 270 | } 271 | 272 | public static void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context spc, int skip) 273 | { 274 | spc.skip_missing = skip; 275 | } 276 | 277 | [StructLayout(LayoutKind.Sequential)] 278 | public struct stbrp_context 279 | { 280 | public int width; 281 | public int height; 282 | public int x; 283 | public int y; 284 | public int bottom_y; 285 | } 286 | 287 | [StructLayout(LayoutKind.Sequential)] 288 | public struct stbrp_node 289 | { 290 | public byte x; 291 | } 292 | 293 | [StructLayout(LayoutKind.Sequential)] 294 | public struct stbrp_rect 295 | { 296 | public int x; 297 | public int y; 298 | public int id; 299 | public int w; 300 | public int h; 301 | public int was_packed; 302 | } 303 | 304 | public class stbtt_pack_context 305 | { 306 | public uint h_oversample; 307 | public int height; 308 | public void* nodes; 309 | public void* pack_info; 310 | public int padding; 311 | public byte* pixels; 312 | public int skip_missing; 313 | public int stride_in_bytes; 314 | public void* user_allocator_context; 315 | public uint v_oversample; 316 | public int width; 317 | } 318 | 319 | [StructLayout(LayoutKind.Sequential)] 320 | public struct stbtt_pack_range 321 | { 322 | public float font_size; 323 | public int first_unicode_codepoint_in_range; 324 | public int* array_of_unicode_codepoints; 325 | public int num_chars; 326 | public stbtt_packedchar* chardata_for_range; 327 | public byte h_oversample; 328 | public byte v_oversample; 329 | } 330 | } 331 | } -------------------------------------------------------------------------------- /src/StbTrueType.OldRasterizer.cs: -------------------------------------------------------------------------------- 1 | using Hebron.Runtime; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace StbTrueTypeSharp 5 | { 6 | unsafe partial class StbTrueType 7 | { 8 | public static stbtt__active_edge_old_rasterizer* stbtt__new_active_old_rasterizer(stbtt__hheap* hh, stbtt__edge* e, int off_x, float start_point, void* userdata) 9 | { 10 | stbtt__active_edge_old_rasterizer* z = (stbtt__active_edge_old_rasterizer*)(stbtt__hheap_alloc(hh, (ulong)(sizeof(stbtt__active_edge_old_rasterizer)), userdata)); 11 | float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 12 | if (z == null) 13 | return z; 14 | if ((dxdy) < (0)) 15 | z->dx = (int)(-((int)(CRuntime.floor((double)((1 << 10) * -dxdy))))); 16 | else 17 | z->dx = ((int)(CRuntime.floor((double)((1 << 10) * dxdy)))); 18 | 19 | z->x = ((int)(CRuntime.floor((double)((1 << 10) * e->x0 + z->dx * (start_point - e->y0))))); 20 | z->x -= (int)(off_x * (1 << 10)); 21 | z->ey = (float)(e->y1); 22 | z->next = null; 23 | z->direction = (int)((e->invert) != 0 ? 1 : -1); 24 | return z; 25 | } 26 | 27 | public static void stbtt__fill_active_edges_old_rasterizer(byte* scanline, int len, stbtt__active_edge_old_rasterizer* e, int max_weight) 28 | { 29 | int x0 = 0; 30 | int w = 0; 31 | while ((e) != null) 32 | { 33 | if ((w) == (0)) 34 | { 35 | x0 = (int)(e->x); 36 | w += (int)(e->direction); 37 | } 38 | else 39 | { 40 | int x1 = e->x; 41 | w += (int)(e->direction); 42 | if ((w) == (0)) 43 | { 44 | int i = x0 >> 10; 45 | int j = x1 >> 10; 46 | if (((i) < (len)) && ((j) >= (0))) 47 | { 48 | if ((i) == (j)) 49 | { 50 | scanline[i] = (byte)(scanline[i] + (byte)((x1 - x0) * max_weight >> 10)); 51 | } 52 | else 53 | { 54 | if ((i) >= (0)) 55 | scanline[i] = (byte)(scanline[i] + (byte)((((1 << 10) - (x0 & ((1 << 10) - 1))) * max_weight) >> 10)); 56 | else 57 | i = (int)(-1); 58 | if ((j) < (len)) 59 | scanline[j] = (byte)(scanline[j] + (byte)(((x1 & ((1 << 10) - 1)) * max_weight) >> 10)); 60 | else 61 | j = (int)(len); 62 | for (++i; (i) < (j); ++i) 63 | { 64 | scanline[i] = (byte)(scanline[i] + (byte)(max_weight)); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | e = e->next; 72 | } 73 | } 74 | 75 | public static void stbtt__rasterize_sorted_edges_old_rasterizer(stbtt__bitmap* result, stbtt__edge* e, int n, int vsubsample, int off_x, int off_y, void* userdata) 76 | { 77 | usedOldRasterizer = true; 78 | 79 | var hh = new stbtt__hheap(); 80 | stbtt__active_edge_old_rasterizer* active = null; 81 | int y = 0; int j = 0; 82 | int max_weight = (255 / vsubsample); 83 | int s = 0; 84 | byte* scanline_data = stackalloc byte[512]; 85 | byte* scanline; 86 | 87 | if ((result->w) > (512)) 88 | scanline = (byte*)(CRuntime.malloc((ulong)(result->w))); 89 | else 90 | scanline = scanline_data; 91 | y = (int)(off_y * vsubsample); 92 | e[n].y0 = (float)((off_y + result->h) * (float)(vsubsample) + 1); 93 | while ((j) < (result->h)) 94 | { 95 | CRuntime.memset(scanline, (int)(0), (ulong)(result->w)); 96 | for (s = (int)(0); (s) < (vsubsample); ++s) 97 | { 98 | float scan_y = y + 0.5f; 99 | stbtt__active_edge_old_rasterizer** step = &active; 100 | while ((*step) != null) 101 | { 102 | stbtt__active_edge_old_rasterizer* z = *step; 103 | if ((z->ey) <= (scan_y)) 104 | { 105 | *step = z->next; 106 | z->direction = (int)(0); 107 | stbtt__hheap_free(&hh, z); 108 | } 109 | else 110 | { 111 | z->x += (int)(z->dx); 112 | step = &((*step)->next); 113 | } 114 | } 115 | 116 | for (; ; ) 117 | { 118 | int changed = 0; 119 | step = &active; 120 | while (((*step) != null) && (((*step)->next) != null)) 121 | { 122 | if (((*step)->x) > ((*step)->next->x)) 123 | { 124 | stbtt__active_edge_old_rasterizer* t = *step; 125 | stbtt__active_edge_old_rasterizer* q = t->next; 126 | t->next = q->next; 127 | q->next = t; 128 | *step = q; 129 | changed = (int)(1); 130 | } 131 | 132 | step = &(*step)->next; 133 | } 134 | 135 | if (changed == 0) 136 | break; 137 | } 138 | 139 | while ((e->y0) <= (scan_y)) 140 | { 141 | if ((e->y1) > (scan_y)) 142 | { 143 | stbtt__active_edge_old_rasterizer* z = stbtt__new_active_old_rasterizer(&hh, e, (int)(off_x), (float)(scan_y), userdata); 144 | if (z != null) 145 | { 146 | if ((active) == (null)) 147 | active = z; 148 | else if ((z->x) < (active->x)) 149 | { 150 | z->next = active; 151 | active = z; 152 | } 153 | else 154 | { 155 | stbtt__active_edge_old_rasterizer* p = active; 156 | while (((p->next) != null) && ((p->next->x) < (z->x))) 157 | { 158 | p = p->next; 159 | } 160 | 161 | z->next = p->next; 162 | p->next = z; 163 | } 164 | } 165 | } 166 | 167 | ++e; 168 | } 169 | 170 | if ((active) != null) 171 | stbtt__fill_active_edges_old_rasterizer(scanline, (int)(result->w), active, (int)(max_weight)); 172 | ++y; 173 | } 174 | 175 | CRuntime.memcpy(result->pixels + j * result->stride, scanline, (ulong)(result->w)); 176 | ++j; 177 | } 178 | 179 | stbtt__hheap_cleanup(&hh, userdata); 180 | if (scanline != scanline_data) 181 | CRuntime.free(scanline); 182 | } 183 | 184 | [StructLayout(LayoutKind.Sequential)] 185 | public struct stbtt__active_edge_old_rasterizer 186 | { 187 | public stbtt__active_edge_old_rasterizer* next; 188 | public int x; 189 | public int dx; 190 | public float ey; 191 | public int direction; 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /src/StbTrueType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Hebron.Runtime; 4 | 5 | namespace StbTrueTypeSharp 6 | { 7 | #if !STBSHARP_INTERNAL 8 | public 9 | #else 10 | internal 11 | #endif 12 | static unsafe partial class StbTrueType 13 | { 14 | internal static bool usedOldRasterizer = false; 15 | 16 | public static int NativeAllocations => MemoryStats.Allocations; 17 | 18 | public class stbtt_fontinfo : IDisposable 19 | { 20 | public stbtt__buf cff; 21 | public stbtt__buf charstrings; 22 | public byte* data = null; 23 | public stbtt__buf fdselect; 24 | public stbtt__buf fontdicts; 25 | public int fontstart; 26 | public int glyf; 27 | public int gpos; 28 | public stbtt__buf gsubrs; 29 | public int head; 30 | public int hhea; 31 | public int hmtx; 32 | public int index_map; 33 | public int indexToLocFormat; 34 | public bool isDataCopy; 35 | public int kern; 36 | public int loca; 37 | public int numGlyphs; 38 | public stbtt__buf subrs; 39 | public int svg; 40 | public void* userdata; 41 | public bool useOldRasterizer; 42 | 43 | public void Dispose() 44 | { 45 | Dispose(true); 46 | } 47 | 48 | ~stbtt_fontinfo() 49 | { 50 | Dispose(false); 51 | } 52 | 53 | protected virtual void Dispose(bool disposing) 54 | { 55 | if (isDataCopy && data != null) 56 | { 57 | CRuntime.free(data); 58 | data = null; 59 | } 60 | } 61 | } 62 | 63 | public static uint stbtt__find_table(byte* data, uint fontstart, string tag) 64 | { 65 | int num_tables = ttUSHORT(data + fontstart + 4); 66 | var tabledir = fontstart + 12; 67 | int i; 68 | for (i = 0; i < num_tables; ++i) 69 | { 70 | var loc = (uint)(tabledir + 16 * i); 71 | if ((data + loc + 0)[0] == tag[0] && (data + loc + 0)[1] == tag[1] && 72 | (data + loc + 0)[2] == tag[2] && (data + loc + 0)[3] == tag[3]) 73 | return ttULONG(data + loc + 8); 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | public static bool stbtt_BakeFontBitmap(byte[] ttf, int offset, float pixel_height, byte[] pixels, int pw, 80 | int ph, 81 | int first_char, int num_chars, stbtt_bakedchar[] chardata) 82 | { 83 | fixed (byte* ttfPtr = ttf) 84 | { 85 | fixed (byte* pixelsPtr = pixels) 86 | { 87 | fixed (stbtt_bakedchar* chardataPtr = chardata) 88 | { 89 | var result = stbtt_BakeFontBitmap(ttfPtr, offset, pixel_height, pixelsPtr, pw, ph, first_char, 90 | num_chars, 91 | chardataPtr); 92 | 93 | return result != 0; 94 | } 95 | } 96 | } 97 | } 98 | 99 | /// 100 | /// Creates and initializes a font from ttf/otf/ttc data 101 | /// 102 | /// 103 | /// 104 | /// null if the data was invalid 105 | public static stbtt_fontinfo CreateFont(byte[] data, int offset) 106 | { 107 | var dataCopy = (byte*)CRuntime.malloc(data.Length); 108 | Marshal.Copy(data, 0, new IntPtr(dataCopy), data.Length); 109 | 110 | var info = new stbtt_fontinfo 111 | { 112 | isDataCopy = true 113 | }; 114 | 115 | if (stbtt_InitFont_internal(info, dataCopy, offset) == 0) 116 | { 117 | info.Dispose(); 118 | return null; 119 | } 120 | 121 | return info; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /src/StbTrueTypeSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | StbTrueTypeSharpTeam 4 | StbTrueTypeSharp 5 | netstandard2.0;net471 6 | C# port of stb_truetype.h 7 | https://github.com/rds1983/StbSharp#license 8 | https://github.com/rds1983/StbSharp 9 | 1.26.12 10 | true 11 | 12 | 13 | true 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/Resources/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbTrueTypeSharp/91b2755cb4e531808cd3008be2cdc5658b77b5a8/tests/Resources/DroidSans.ttf -------------------------------------------------------------------------------- /tests/StbTrueTypeSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using NUnit.Framework; 6 | using StbTrueTypeSharp.Tests.Utility; 7 | using static StbTrueTypeSharp.StbTrueType; 8 | using FontsList = System.Collections.Generic.List; 9 | 10 | namespace StbTrueTypeSharp.Tests 11 | { 12 | [TestFixture] 13 | public unsafe class Tests 14 | { 15 | private static readonly Assembly _assembly = typeof(Tests).Assembly; 16 | 17 | /// 18 | /// Makes sure Exception is thrown if font file lacks index map 19 | /// 20 | [Test] 21 | public void TestNoIndexMap() 22 | { 23 | var ttfData = File.ReadAllBytes(@"C:\Windows\Fonts\webdings.ttf"); 24 | Assert.Throws(() => 25 | { 26 | var fontInfo = CreateFont(ttfData, 0); 27 | }); 28 | } 29 | 30 | [Test] 31 | public void TestCreationAndDispose() 32 | { 33 | var ttfData = _assembly.ReadResourceAsBytes("DroidSans.ttf"); 34 | var fontInfo = CreateFont(ttfData, 0); 35 | Assert.NotNull(fontInfo); 36 | Assert.IsTrue(fontInfo.isDataCopy); 37 | fontInfo.Dispose(); 38 | Assert.IsTrue(fontInfo.data == null); 39 | } 40 | 41 | [Test] 42 | public unsafe void TestLoadFontCollection() 43 | { 44 | string fontsPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); 45 | string someTtcPath = Directory.EnumerateFiles(fontsPath, "*.ttc", new EnumerationOptions() 46 | { 47 | AttributesToSkip = FileAttributes.Directory, 48 | MatchCasing = MatchCasing.CaseInsensitive, 49 | MatchType = MatchType.Simple, 50 | RecurseSubdirectories = false, 51 | IgnoreInaccessible = true, 52 | ReturnSpecialDirectories = false 53 | }).FirstOrDefault(); 54 | 55 | if (someTtcPath == null) 56 | Assert.Inconclusive("You don't have a ttc font installed on your computer, but this test requires it."); 57 | 58 | byte[] ttcContent = File.ReadAllBytes(someTtcPath); 59 | // Assert.NotNull(someTtc); 60 | FontsList fonts; 61 | int numberOfFonts; 62 | 63 | fixed (byte* ttcPtr = ttcContent) 64 | { 65 | numberOfFonts = stbtt_GetNumberOfFonts(ttcPtr); 66 | fonts = new FontsList(numberOfFonts); 67 | for (int i = 0; i < numberOfFonts; i++) 68 | { 69 | int offset = stbtt_GetFontOffsetForIndex(ttcPtr, i); 70 | fonts.Add(CreateFont(ttcContent, offset)); 71 | } 72 | } 73 | 74 | Assert.AreEqual(numberOfFonts, fonts.Count); 75 | } 76 | 77 | private void TestRasterize(stbtt_fontinfo fontInfo, string text, float size) 78 | { 79 | int iascent, idescent, ilineGap; 80 | stbtt_GetFontVMetrics(fontInfo, &iascent, &idescent, &ilineGap); 81 | 82 | var scale = stbtt_ScaleForPixelHeight(fontInfo, 32.0f); 83 | var ascent = iascent * scale; 84 | var descent = idescent * scale; 85 | var lineGap = ilineGap * scale; 86 | 87 | var lineHeight = ascent - descent + lineGap; 88 | 89 | Assert.IsTrue(lineHeight.EpsilonEquals(32.0f)); 90 | 91 | for (var i = 0; i < text.Length; ++i) 92 | { 93 | var c = text[i]; 94 | 95 | if (char.IsWhiteSpace(c)) 96 | { 97 | continue; 98 | } 99 | 100 | var glyphId = stbtt_FindGlyphIndex(fontInfo, c); 101 | Assert.NotZero(glyphId); 102 | 103 | int advanceWidth, leftSideBearing; 104 | stbtt_GetGlyphHMetrics(fontInfo, glyphId, &advanceWidth, &leftSideBearing); 105 | 106 | int x0, y0, x1, y1; 107 | stbtt_GetGlyphBitmapBox(fontInfo, glyphId, scale, scale, &x0, &y0, &x1, &y1); 108 | 109 | var width = x1 - x0; 110 | var height = y1 - y0; 111 | 112 | Assert.NotZero(width); 113 | Assert.NotZero(height); 114 | var data = new byte[width * height]; 115 | 116 | fixed (byte* ptr = data) 117 | { 118 | stbtt_MakeGlyphBitmap(fontInfo, ptr, width, height, width, scale, scale, glyphId); 119 | } 120 | } 121 | } 122 | 123 | [Test] 124 | public void TestNewRasterizer() 125 | { 126 | var ttfData = _assembly.ReadResourceAsBytes("DroidSans.ttf"); 127 | var fontInfo = CreateFont(ttfData, 0); 128 | Assert.NotNull(fontInfo); 129 | Assert.IsTrue(fontInfo.isDataCopy); 130 | 131 | TestRasterize(fontInfo, "Hello, World!", 32.0f); 132 | 133 | Assert.IsFalse(usedOldRasterizer); 134 | 135 | fontInfo.Dispose(); 136 | Assert.IsTrue(fontInfo.data == null); 137 | } 138 | 139 | [Test] 140 | public void TestOldRasterizer() 141 | { 142 | var ttfData = _assembly.ReadResourceAsBytes("DroidSans.ttf"); 143 | var fontInfo = CreateFont(ttfData, 0); 144 | Assert.NotNull(fontInfo); 145 | Assert.IsTrue(fontInfo.isDataCopy); 146 | 147 | fontInfo.useOldRasterizer = true; 148 | 149 | TestRasterize(fontInfo, "Hello, World!", 32.0f); 150 | 151 | Assert.IsTrue(usedOldRasterizer); 152 | 153 | fontInfo.Dispose(); 154 | Assert.IsTrue(fontInfo.data == null); 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /tests/Utility/Mathematics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbTrueTypeSharp.Tests.Utility 4 | { 5 | internal static class Mathematics 6 | { 7 | /// 8 | /// The value for which all absolute numbers smaller than are considered equal to zero. 9 | /// 10 | public const float ZeroTolerance = 1e-6f; 11 | 12 | /// 13 | /// Compares two floating point numbers based on an epsilon zero tolerance. 14 | /// 15 | /// The first number to compare. 16 | /// The second number to compare. 17 | /// The epsilon value to use for zero tolerance. 18 | /// true if is within epsilon of ; otherwise, false. 19 | public static bool EpsilonEquals(this float left, float right, float epsilon = ZeroTolerance) 20 | { 21 | return Math.Abs(left - right) <= epsilon; 22 | } 23 | 24 | public static bool IsEpsilonZero(this float a) 25 | { 26 | return a.EpsilonEquals(0.0f); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Utility/Res.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | 4 | namespace StbTrueTypeSharp.Tests.Utility 5 | { 6 | /// 7 | /// Resource utility 8 | /// 9 | public static class Res 10 | { 11 | /// 12 | /// Open assembly resource stream by relative name 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static Stream OpenResourceStream(this Assembly assembly, string assetName) 18 | { 19 | var path = assembly.GetName().Name + ".Resources." + assetName; 20 | 21 | // Once you figure out the name, pass it in as the argument here. 22 | var stream = assembly.GetManifestResourceStream(path); 23 | 24 | return stream; 25 | } 26 | 27 | /// 28 | /// Reads assembly resource as byte array by relative name 29 | /// 30 | /// 31 | /// 32 | /// 33 | public static byte[] ReadResourceAsBytes(this Assembly assembly, string assetName) 34 | { 35 | var ms = new MemoryStream(); 36 | using (var input = assembly.OpenResourceStream(assetName)) 37 | { 38 | input.CopyTo(ms); 39 | 40 | return ms.ToArray(); 41 | } 42 | } 43 | 44 | /// 45 | /// Reads assembly resource as string by relative name 46 | /// 47 | /// 48 | /// 49 | /// 50 | public static string ReadResourceAsString(this Assembly assembly, string assetName) 51 | { 52 | string result; 53 | using (var input = assembly.OpenResourceStream(assetName)) 54 | { 55 | using (var textReader = new StreamReader(input)) 56 | { 57 | result = textReader.ReadToEnd(); 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | } 64 | } --------------------------------------------------------------------------------