├── .github └── workflows │ └── build-and-publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── StbRectPackSharp.sln ├── generation ├── StbRectPackSharp.Generator.sln └── StbRectPackSharp.Generator │ ├── App.config │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── StbRectPackSharp.Generator.csproj │ └── stb_rect_pack.h ├── src ├── CRuntime.cs ├── Packer.cs ├── StbRectPack.Generated.cs ├── StbRectPack.cs └── StbRectPackSharp.csproj └── tests ├── StbRectPackSharp.Tests ├── StbRectPackSharp.Tests.csproj └── Tests.cs └── StbRectPackSharp.VisualizePacking ├── Fonts └── DroidSans.ttf ├── Program.cs ├── StbRectPackSharp.VisualizePacking.csproj └── VisualizerGame.cs /.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: '3.1.x' 18 | - name: Build StbRectPackSharp 19 | run: dotnet build src\StbRectPackSharp.csproj --configuration Release 20 | - name: Install NuGet 21 | uses: NuGet/setup-nuget@v1 22 | - name: Publish StbRectPackSharp to NuGet 23 | run: nuget.exe push src\bin\Release\StbRectPackSharp.*.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 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StbRectPackSharp 2 | [![NuGet](https://img.shields.io/nuget/v/StbRectPackSharp.svg)](https://www.nuget.org/packages/StbRectPackSharp/) 3 | ![Build & Publish](https://github.com/StbSharp/StbRectPackSharp/workflows/Build%20&%20Publish/badge.svg) 4 | [![Chat](https://img.shields.io/discord/628186029488340992.svg)](https://discord.gg/ZeHxhCY) 5 | 6 | StbRectPackSharp is C# port of the [stb_rect_pack.h](https://github.com/nothings/stb/blob/master/stb_rect_pack.h), which is C library to pack rectangles on atlas. 7 | 8 | # Adding Reference 9 | There are two ways of referencing StbRectPackSharp in the project: 10 | 1. Through nuget: https://www.nuget.org/packages/StbRectPackSharp/ 11 | 2. As submodule: 12 | 13 | a. `git submodule add https://github.com/rds1983/StbRectPackSharp.git` 14 | 15 | b. Now there are two options: 16 | 17 | * Add src/StbRectPackSharp.csproj to the solution 18 | 19 | * Include *.cs from folder "src" directly in the project. In this case, it might make sense to add STBSHARP_INTERNAL build compilation symbol to the project, so StbRectPackSharp classes would become internal. 20 | 21 | # Usage 22 | StbRectPackSharp exposes API similar to [stb_rect_pack.h](https://github.com/nothings/stb/blob/master/stb_rect_pack.h). 23 | 24 | Also it has utility class Packer. 25 | 26 | Sample usage code 27 | ```c# 28 | // Create packer with size 256x256 29 | Packer packer = new Packer(256, 256); 30 | 31 | // Pack a few rectangles 32 | // data1, data2, data3 are arbitrary user objects that will be stored within instances of PackRectangle class 33 | // PackRect returns either object of PackerRectangle class(packing was succesful) or null(no more place) 34 | packer.PackRect(10, 10, data1); 35 | packer.PackRect(15, 10, data2); 36 | packer.PackRect(2, 2, data3); 37 | 38 | // Enumerate packed rectangles 39 | foreach(PackerRectangle packRect in packer.PackRectangles) 40 | { 41 | // ... 42 | } 43 | ``` 44 | 45 | If there's no more space to fit the new rectangle, then PackRect method will return null. 46 | 47 | It could be addressed by creating newer and bigger Packer. 48 | 49 | I.e. 50 | ```c# 51 | PackerRectangle pr = packer.PackRect(800, 600, data4); 52 | 53 | // If pr is null, it means there's no place for the new rect 54 | // Double the size of the packer until the new rectangle will fit 55 | while(pr == null) 56 | { 57 | Packer newPacker = new Packer(packer.Width * 2, packer.Height * 2); 58 | 59 | // Place existing rectangles 60 | foreach(PackerRectangle existingRect in packer.PackRectangles) 61 | { 62 | newPacker.PackRect(existingRect.Width, existingRect.Height, existingRect.Data); 63 | } 64 | 65 | // Now dispose old packer and assign new one 66 | packer.Dispose(); 67 | packer = newPacker; 68 | 69 | // Try to fit the rectangle again 70 | pr = packer.PackRect(800, 600, data4); 71 | } 72 | ``` 73 | 74 | See also sample [VisualizePacking](https://github.com/StbSharp/StbRectPackSharp/tree/master/tests/StbRectPackSharp.VisualizePacking) that uses above code. 75 | 76 | # License 77 | Public Domain 78 | 79 | # Credits 80 | https://github.com/nothings/stb 81 | -------------------------------------------------------------------------------- /StbRectPackSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{AD5041AC-C96B-42F8-BC8C-BFFD2090B364}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbRectPackSharp", "src\StbRectPackSharp.csproj", "{E8BAA838-C3F5-4854-A449-B9ECE5DF84C9}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbRectPackSharp.VisualizePacking", "tests\StbRectPackSharp.VisualizePacking\StbRectPackSharp.VisualizePacking.csproj", "{4FA734D9-88AE-4B0A-A3FF-93E7C026F103}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StbRectPackSharp.Tests", "tests\StbRectPackSharp.Tests\StbRectPackSharp.Tests.csproj", "{1EB3D13F-1AAA-4B38-B5F2-0BCA8465D500}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {E8BAA838-C3F5-4854-A449-B9ECE5DF84C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E8BAA838-C3F5-4854-A449-B9ECE5DF84C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E8BAA838-C3F5-4854-A449-B9ECE5DF84C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E8BAA838-C3F5-4854-A449-B9ECE5DF84C9}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {4FA734D9-88AE-4B0A-A3FF-93E7C026F103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {4FA734D9-88AE-4B0A-A3FF-93E7C026F103}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {4FA734D9-88AE-4B0A-A3FF-93E7C026F103}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {4FA734D9-88AE-4B0A-A3FF-93E7C026F103}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {1EB3D13F-1AAA-4B38-B5F2-0BCA8465D500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {1EB3D13F-1AAA-4B38-B5F2-0BCA8465D500}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {1EB3D13F-1AAA-4B38-B5F2-0BCA8465D500}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {1EB3D13F-1AAA-4B38-B5F2-0BCA8465D500}.Release|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {4FA734D9-88AE-4B0A-A3FF-93E7C026F103} = {AD5041AC-C96B-42F8-BC8C-BFFD2090B364} 38 | {1EB3D13F-1AAA-4B38-B5F2-0BCA8465D500} = {AD5041AC-C96B-42F8-BC8C-BFFD2090B364} 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {C9C812E7-F89A-4B6E-8F9D-981D5F8BBAB3} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /generation/StbRectPackSharp.Generator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sichem", "..\..\Sichem\Sichem\Sichem.csproj", "{71352218-91C4-490E-A650-AB17C48D0300}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StbRectPackSharp.Generator", "StbRectPackSharp.Generator\StbRectPackSharp.Generator.csproj", "{2F372A46-2B31-49ED-BEAA-B7D6E72CD983}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|Mixed Platforms = Debug|Mixed Platforms 14 | Debug|x64 = Debug|x64 15 | Release|Any CPU = Release|Any CPU 16 | Release|Mixed Platforms = Release|Mixed Platforms 17 | Release|x64 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {71352218-91C4-490E-A650-AB17C48D0300}.Debug|Any CPU.ActiveCfg = Debug|x64 21 | {71352218-91C4-490E-A650-AB17C48D0300}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 22 | {71352218-91C4-490E-A650-AB17C48D0300}.Debug|Mixed Platforms.Build.0 = Debug|x64 23 | {71352218-91C4-490E-A650-AB17C48D0300}.Debug|x64.ActiveCfg = Debug|x64 24 | {71352218-91C4-490E-A650-AB17C48D0300}.Debug|x64.Build.0 = Debug|x64 25 | {71352218-91C4-490E-A650-AB17C48D0300}.Release|Any CPU.ActiveCfg = Release|x64 26 | {71352218-91C4-490E-A650-AB17C48D0300}.Release|Mixed Platforms.ActiveCfg = Release|x64 27 | {71352218-91C4-490E-A650-AB17C48D0300}.Release|Mixed Platforms.Build.0 = Release|x64 28 | {71352218-91C4-490E-A650-AB17C48D0300}.Release|x64.ActiveCfg = Release|x64 29 | {71352218-91C4-490E-A650-AB17C48D0300}.Release|x64.Build.0 = Release|x64 30 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Debug|Any CPU.ActiveCfg = Debug|x64 31 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 32 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Debug|Mixed Platforms.Build.0 = Debug|x64 33 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Debug|x64.ActiveCfg = Debug|x64 34 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Debug|x64.Build.0 = Debug|x64 35 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Release|Any CPU.ActiveCfg = Release|x64 36 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Release|Mixed Platforms.ActiveCfg = Release|x64 37 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Release|Mixed Platforms.Build.0 = Release|x64 38 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Release|x64.ActiveCfg = Release|x64 39 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983}.Release|x64.Build.0 = Release|x64 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(ExtensibilityGlobals) = postSolution 45 | SolutionGuid = {ACF4E34C-5E6B-44ED-AA80-A1C43BA55B85} 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /generation/StbRectPackSharp.Generator/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /generation/StbRectPackSharp.Generator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using Sichem; 6 | 7 | namespace StbImageResizeSharp.Generator 8 | { 9 | class Program 10 | { 11 | private static void Write(Dictionary input, StringBuilder output) 12 | { 13 | foreach (var pair in input) 14 | { 15 | output.Append(pair.Value); 16 | } 17 | } 18 | 19 | private static string PostProcess(string data) 20 | { 21 | data = Utility.ReplaceNativeCalls(data); 22 | 23 | data = data.Replace("((void*)(0))", "null"); 24 | data = data.Replace("((void *)(0))", "null"); 25 | data = data.Replace("default: ;", "default: throw new Exception(\"Mode \" + context->init_mode + \" is not supported.\");"); 26 | data = data.Replace("(int)(((p->w) > (q->w))?-1:((p->w) < (q->w)))", 27 | "(int)(((p->w) > (q->w))?-1:((p->w) < (q->w))?1:0)"); 28 | data = data.Replace("(int)(((p->was_packed) < (q->was_packed))?-1:((p->was_packed) > (q->was_packed)))", 29 | "(int)(((p->was_packed) < (q->was_packed))?-1:((p->was_packed) > (q->was_packed))?1:0)"); 30 | data = data.Replace("(int)(!(((rects[i].x) == (0xffff)) && ((rects[i].y) == (0xffff))))", 31 | "(((rects[i].x) == (0xffff)) && ((rects[i].y) == (0xffff)))?0:1"); 32 | 33 | return data; 34 | } 35 | 36 | static void Process() 37 | { 38 | var parameters = new ConversionParameters 39 | { 40 | InputPath = @"stb_rect_pack.h", 41 | Defines = new string[] 42 | { 43 | "STB_RECT_PACK_IMPLEMENTATION" 44 | }, 45 | SkipStructs = new string[] 46 | { 47 | "stbrp_context" 48 | }, 49 | SkipGlobalVariables = new string[] 50 | { 51 | }, 52 | SkipFunctions = new string[] 53 | { 54 | }, 55 | Classes = new string[] 56 | { 57 | }, 58 | GlobalArrays = new string[] 59 | { 60 | } 61 | }; 62 | 63 | var cp = new ClangParser(); 64 | 65 | var result = cp.Process(parameters); 66 | 67 | // Write output 68 | var sb = new StringBuilder(); 69 | sb.AppendLine(string.Format("// Generated by Sichem at {0}", DateTime.Now)); 70 | sb.AppendLine(); 71 | 72 | sb.AppendLine("using System;"); 73 | sb.AppendLine("using System.Runtime.InteropServices;"); 74 | 75 | sb.AppendLine(); 76 | 77 | sb.Append("namespace StbRectPackSharp\n{\n\t"); 78 | sb.AppendLine("unsafe partial class StbRectPack\n\t{"); 79 | 80 | Write(result.Constants, sb); 81 | Write(result.GlobalVariables, sb); 82 | Write(result.Enums, sb); 83 | Write(result.Structs, sb); 84 | Write(result.Methods, sb); 85 | 86 | sb.Append("}\n}"); 87 | var data = sb.ToString(); 88 | 89 | // Post processing 90 | Logger.Info("Post processing..."); 91 | data = PostProcess(data); 92 | 93 | File.WriteAllText(@"..\..\..\..\..\src\StbRectPack.Generated.cs", data); 94 | } 95 | 96 | static void Main(string[] args) 97 | { 98 | try 99 | { 100 | Process(); 101 | } 102 | catch (Exception ex) 103 | { 104 | Console.WriteLine(ex.Message); 105 | Console.WriteLine(ex.StackTrace); 106 | } 107 | 108 | Console.WriteLine("Finished. Press any key to quit."); 109 | Console.ReadKey(); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /generation/StbRectPackSharp.Generator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Generator")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Generator")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f1343b90-bd64-48d3-9cd2-e1d954dc3da4")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /generation/StbRectPackSharp.Generator/StbRectPackSharp.Generator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2F372A46-2B31-49ED-BEAA-B7D6E72CD983} 8 | Exe 9 | Properties 10 | StbImageResizeSharp.Generator 11 | StbImageResizeSharp.Generator 12 | v4.5 13 | 512 14 | ccc2377f 15 | 16 | 17 | true 18 | bin\x64\Debug\ 19 | DEBUG;TRACE 20 | full 21 | x64 22 | prompt 23 | MinimumRecommendedRules.ruleset 24 | 25 | 26 | bin\x64\Release\ 27 | TRACE 28 | true 29 | pdbonly 30 | x64 31 | prompt 32 | MinimumRecommendedRules.ruleset 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {71352218-91c4-490e-a650-ab17c48d0300} 54 | Sichem 55 | 56 | 57 | 58 | 59 | PreserveNewest 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /generation/StbRectPackSharp.Generator/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v1.00 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // github:IntellectualKitty 31 | // 32 | // Bugfixes / warning fixes 33 | // Jeremy Jaussaud 34 | // Fabian Giesen 35 | // 36 | // Version history: 37 | // 38 | // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles 39 | // 0.99 (2019-02-07) warning fixes 40 | // 0.11 (2017-03-03) return packing success/fail result 41 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 42 | // 0.09 (2016-08-27) fix compiler warnings 43 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 44 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 45 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 46 | // 0.05: added STBRP_ASSERT to allow replacing assert 47 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 48 | // 0.01: initial release 49 | // 50 | // LICENSE 51 | // 52 | // See end of file for license information. 53 | 54 | ////////////////////////////////////////////////////////////////////////////// 55 | // 56 | // INCLUDE SECTION 57 | // 58 | 59 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 60 | #define STB_INCLUDE_STB_RECT_PACK_H 61 | 62 | #define STB_RECT_PACK_VERSION 1 63 | 64 | #ifdef STBRP_STATIC 65 | #define STBRP_DEF static 66 | #else 67 | #define STBRP_DEF extern 68 | #endif 69 | 70 | #ifdef __cplusplus 71 | extern "C" { 72 | #endif 73 | 74 | typedef struct stbrp_context stbrp_context; 75 | typedef struct stbrp_node stbrp_node; 76 | typedef struct stbrp_rect stbrp_rect; 77 | 78 | #ifdef STBRP_LARGE_RECTS 79 | typedef int stbrp_coord; 80 | #else 81 | typedef unsigned short stbrp_coord; 82 | #endif 83 | 84 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 85 | // Assign packed locations to rectangles. The rectangles are of type 86 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 87 | // are 'num_rects' many of them. 88 | // 89 | // Rectangles which are successfully packed have the 'was_packed' flag 90 | // set to a non-zero value and 'x' and 'y' store the minimum location 91 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 92 | // if you imagine y increasing downwards). Rectangles which do not fit 93 | // have the 'was_packed' flag set to 0. 94 | // 95 | // You should not try to access the 'rects' array from another thread 96 | // while this function is running, as the function temporarily reorders 97 | // the array while it executes. 98 | // 99 | // To pack into another rectangle, you need to call stbrp_init_target 100 | // again. To continue packing into the same rectangle, you can call 101 | // this function again. Calling this multiple times with multiple rect 102 | // arrays will probably produce worse packing results than calling it 103 | // a single time with the full rectangle array, but the option is 104 | // available. 105 | // 106 | // The function returns 1 if all of the rectangles were successfully 107 | // packed and 0 otherwise. 108 | 109 | struct stbrp_rect 110 | { 111 | // reserved for your use: 112 | int id; 113 | 114 | // input: 115 | stbrp_coord w, h; 116 | 117 | // output: 118 | stbrp_coord x, y; 119 | int was_packed; // non-zero if valid packing 120 | 121 | }; // 16 bytes, nominally 122 | 123 | 124 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 125 | // Initialize a rectangle packer to: 126 | // pack a rectangle that is 'width' by 'height' in dimensions 127 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 128 | // 129 | // You must call this function every time you start packing into a new target. 130 | // 131 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 132 | // the following stbrp_pack_rects() call (or calls), but can be freed after 133 | // the call (or calls) finish. 134 | // 135 | // Note: to guarantee best results, either: 136 | // 1. make sure 'num_nodes' >= 'width' 137 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 138 | // 139 | // If you don't do either of the above things, widths will be quantized to multiples 140 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 141 | // 142 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 143 | // may run out of temporary storage and be unable to pack some rectangles. 144 | 145 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 146 | // Optionally call this function after init but before doing any packing to 147 | // change the handling of the out-of-temp-memory scenario, described above. 148 | // If you call init again, this will be reset to the default (false). 149 | 150 | 151 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 152 | // Optionally select which packing heuristic the library should use. Different 153 | // heuristics will produce better/worse results for different data sets. 154 | // If you call init again, this will be reset to the default. 155 | 156 | enum 157 | { 158 | STBRP_HEURISTIC_Skyline_default=0, 159 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 160 | STBRP_HEURISTIC_Skyline_BF_sortHeight 161 | }; 162 | 163 | 164 | ////////////////////////////////////////////////////////////////////////////// 165 | // 166 | // the details of the following structures don't matter to you, but they must 167 | // be visible so you can handle the memory allocations for them 168 | 169 | struct stbrp_node 170 | { 171 | stbrp_coord x,y; 172 | stbrp_node *next; 173 | }; 174 | 175 | struct stbrp_context 176 | { 177 | int width; 178 | int height; 179 | int align; 180 | int init_mode; 181 | int heuristic; 182 | int num_nodes; 183 | stbrp_node *active_head; 184 | stbrp_node *free_head; 185 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 186 | }; 187 | 188 | #ifdef __cplusplus 189 | } 190 | #endif 191 | 192 | #endif 193 | 194 | ////////////////////////////////////////////////////////////////////////////// 195 | // 196 | // IMPLEMENTATION SECTION 197 | // 198 | 199 | #ifdef STB_RECT_PACK_IMPLEMENTATION 200 | #ifndef STBRP_SORT 201 | #include 202 | #define STBRP_SORT qsort 203 | #endif 204 | 205 | #ifndef STBRP_ASSERT 206 | #include 207 | #define STBRP_ASSERT(a) 208 | #endif 209 | 210 | #ifdef _MSC_VER 211 | #define STBRP__NOTUSED(v) 212 | #else 213 | #define STBRP__NOTUSED(v) 214 | #endif 215 | 216 | enum 217 | { 218 | STBRP__INIT_skyline = 1 219 | }; 220 | 221 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 222 | { 223 | switch (context->init_mode) { 224 | case STBRP__INIT_skyline: 225 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 226 | context->heuristic = heuristic; 227 | break; 228 | default: 229 | STBRP_ASSERT(0); 230 | } 231 | } 232 | 233 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 234 | { 235 | if (allow_out_of_mem) 236 | // if it's ok to run out of memory, then don't bother aligning them; 237 | // this gives better packing, but may fail due to OOM (even though 238 | // the rectangles easily fit). @TODO a smarter approach would be to only 239 | // quantize once we've hit OOM, then we could get rid of this parameter. 240 | context->align = 1; 241 | else { 242 | // if it's not ok to run out of memory, then quantize the widths 243 | // so that num_nodes is always enough nodes. 244 | // 245 | // I.e. num_nodes * align >= width 246 | // align >= width / num_nodes 247 | // align = ceil(width/num_nodes) 248 | 249 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 250 | } 251 | } 252 | 253 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 254 | { 255 | int i; 256 | #ifndef STBRP_LARGE_RECTS 257 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 258 | #endif 259 | 260 | for (i=0; i < num_nodes-1; ++i) 261 | nodes[i].next = &nodes[i+1]; 262 | nodes[i].next = NULL; 263 | context->init_mode = STBRP__INIT_skyline; 264 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 265 | context->free_head = &nodes[0]; 266 | context->active_head = &context->extra[0]; 267 | context->width = width; 268 | context->height = height; 269 | context->num_nodes = num_nodes; 270 | stbrp_setup_allow_out_of_mem(context, 0); 271 | 272 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 273 | context->extra[0].x = 0; 274 | context->extra[0].y = 0; 275 | context->extra[0].next = &context->extra[1]; 276 | context->extra[1].x = (stbrp_coord) width; 277 | #ifdef STBRP_LARGE_RECTS 278 | context->extra[1].y = (1<<30); 279 | #else 280 | context->extra[1].y = 65535; 281 | #endif 282 | context->extra[1].next = NULL; 283 | } 284 | 285 | // find minimum y position if it starts at x1 286 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 287 | { 288 | stbrp_node *node = first; 289 | int x1 = x0 + width; 290 | int min_y, visited_width, waste_area; 291 | 292 | STBRP__NOTUSED(c); 293 | 294 | STBRP_ASSERT(first->x <= x0); 295 | 296 | #if 0 297 | // skip in case we're past the node 298 | while (node->next->x <= x0) 299 | ++node; 300 | #else 301 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 302 | #endif 303 | 304 | STBRP_ASSERT(node->x <= x0); 305 | 306 | min_y = 0; 307 | waste_area = 0; 308 | visited_width = 0; 309 | while (node->x < x1) { 310 | if (node->y > min_y) { 311 | // raise min_y higher. 312 | // we've accounted for all waste up to min_y, 313 | // but we'll now add more waste for everything we've visted 314 | waste_area += visited_width * (node->y - min_y); 315 | min_y = node->y; 316 | // the first time through, visited_width might be reduced 317 | if (node->x < x0) 318 | visited_width += node->next->x - x0; 319 | else 320 | visited_width += node->next->x - node->x; 321 | } else { 322 | // add waste area 323 | int under_width = node->next->x - node->x; 324 | if (under_width + visited_width > width) 325 | under_width = width - visited_width; 326 | waste_area += under_width * (min_y - node->y); 327 | visited_width += under_width; 328 | } 329 | node = node->next; 330 | } 331 | 332 | *pwaste = waste_area; 333 | return min_y; 334 | } 335 | 336 | typedef struct 337 | { 338 | int x,y; 339 | stbrp_node **prev_link; 340 | } stbrp__findresult; 341 | 342 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 343 | { 344 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 345 | stbrp__findresult fr; 346 | stbrp_node **prev, *node, *tail, **best = NULL; 347 | 348 | // align to multiple of c->align 349 | width = (width + c->align - 1); 350 | width -= width % c->align; 351 | STBRP_ASSERT(width % c->align == 0); 352 | 353 | // if it can't possibly fit, bail immediately 354 | if (width > c->width || height > c->height) { 355 | fr.prev_link = NULL; 356 | fr.x = fr.y = 0; 357 | return fr; 358 | } 359 | 360 | node = c->active_head; 361 | prev = &c->active_head; 362 | while (node->x + width <= c->width) { 363 | int y,waste; 364 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 365 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 366 | // bottom left 367 | if (y < best_y) { 368 | best_y = y; 369 | best = prev; 370 | } 371 | } else { 372 | // best-fit 373 | if (y + height <= c->height) { 374 | // can only use it if it first vertically 375 | if (y < best_y || (y == best_y && waste < best_waste)) { 376 | best_y = y; 377 | best_waste = waste; 378 | best = prev; 379 | } 380 | } 381 | } 382 | prev = &node->next; 383 | node = node->next; 384 | } 385 | 386 | best_x = (best == NULL) ? 0 : (*best)->x; 387 | 388 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 389 | // 390 | // e.g, if fitting 391 | // 392 | // ____________________ 393 | // |____________________| 394 | // 395 | // into 396 | // 397 | // | | 398 | // | ____________| 399 | // |____________| 400 | // 401 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 402 | // 403 | // This makes BF take about 2x the time 404 | 405 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 406 | tail = c->active_head; 407 | node = c->active_head; 408 | prev = &c->active_head; 409 | // find first node that's admissible 410 | while (tail->x < width) 411 | tail = tail->next; 412 | while (tail) { 413 | int xpos = tail->x - width; 414 | int y,waste; 415 | STBRP_ASSERT(xpos >= 0); 416 | // find the left position that matches this 417 | while (node->next->x <= xpos) { 418 | prev = &node->next; 419 | node = node->next; 420 | } 421 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 422 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 423 | if (y + height <= c->height) { 424 | if (y <= best_y) { 425 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 426 | best_x = xpos; 427 | STBRP_ASSERT(y <= best_y); 428 | best_y = y; 429 | best_waste = waste; 430 | best = prev; 431 | } 432 | } 433 | } 434 | tail = tail->next; 435 | } 436 | } 437 | 438 | fr.prev_link = best; 439 | fr.x = best_x; 440 | fr.y = best_y; 441 | return fr; 442 | } 443 | 444 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 445 | { 446 | // find best position according to heuristic 447 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 448 | stbrp_node *node, *cur; 449 | 450 | // bail if: 451 | // 1. it failed 452 | // 2. the best node doesn't fit (we don't always check this) 453 | // 3. we're out of memory 454 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 455 | res.prev_link = NULL; 456 | return res; 457 | } 458 | 459 | // on success, create new node 460 | node = context->free_head; 461 | node->x = (stbrp_coord) res.x; 462 | node->y = (stbrp_coord) (res.y + height); 463 | 464 | context->free_head = node->next; 465 | 466 | // insert the new node into the right starting point, and 467 | // let 'cur' point to the remaining nodes needing to be 468 | // stiched back in 469 | 470 | cur = *res.prev_link; 471 | if (cur->x < res.x) { 472 | // preserve the existing one, so start testing with the next one 473 | stbrp_node *next = cur->next; 474 | cur->next = node; 475 | cur = next; 476 | } else { 477 | *res.prev_link = node; 478 | } 479 | 480 | // from here, traverse cur and free the nodes, until we get to one 481 | // that shouldn't be freed 482 | while (cur->next && cur->next->x <= res.x + width) { 483 | stbrp_node *next = cur->next; 484 | // move the current node to the free list 485 | cur->next = context->free_head; 486 | context->free_head = cur; 487 | cur = next; 488 | } 489 | 490 | // stitch the list back in 491 | node->next = cur; 492 | 493 | if (cur->x < res.x + width) 494 | cur->x = (stbrp_coord) (res.x + width); 495 | 496 | #ifdef _DEBUG 497 | cur = context->active_head; 498 | while (cur->x < context->width) { 499 | STBRP_ASSERT(cur->x < cur->next->x); 500 | cur = cur->next; 501 | } 502 | STBRP_ASSERT(cur->next == NULL); 503 | 504 | { 505 | int count=0; 506 | cur = context->active_head; 507 | while (cur) { 508 | cur = cur->next; 509 | ++count; 510 | } 511 | cur = context->free_head; 512 | while (cur) { 513 | cur = cur->next; 514 | ++count; 515 | } 516 | STBRP_ASSERT(count == context->num_nodes+2); 517 | } 518 | #endif 519 | 520 | return res; 521 | } 522 | 523 | static int rect_height_compare(const void *a, const void *b) 524 | { 525 | const stbrp_rect *p = (const stbrp_rect *) a; 526 | const stbrp_rect *q = (const stbrp_rect *) b; 527 | if (p->h > q->h) 528 | return -1; 529 | if (p->h < q->h) 530 | return 1; 531 | return (p->w > q->w) ? -1 : (p->w < q->w); 532 | } 533 | 534 | static int rect_original_order(const void *a, const void *b) 535 | { 536 | const stbrp_rect *p = (const stbrp_rect *) a; 537 | const stbrp_rect *q = (const stbrp_rect *) b; 538 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 539 | } 540 | 541 | #ifdef STBRP_LARGE_RECTS 542 | #define STBRP__MAXVAL 0xffffffff 543 | #else 544 | #define STBRP__MAXVAL 0xffff 545 | #endif 546 | 547 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 548 | { 549 | int i, all_rects_packed = 1; 550 | 551 | // we use the 'was_packed' field internally to allow sorting/unsorting 552 | for (i=0; i < num_rects; ++i) { 553 | rects[i].was_packed = i; 554 | } 555 | 556 | // sort according to heuristic 557 | STBRP_SORT(rects, num_rects, sizeof(stbrp_rect), rect_height_compare); 558 | 559 | for (i=0; i < num_rects; ++i) { 560 | if (rects[i].w == 0 || rects[i].h == 0) { 561 | rects[i].x = rects[i].y = 0; // empty rect needs no space 562 | } else { 563 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 564 | if (fr.prev_link) { 565 | rects[i].x = (stbrp_coord) fr.x; 566 | rects[i].y = (stbrp_coord) fr.y; 567 | } else { 568 | rects[i].x = rects[i].y = STBRP__MAXVAL; 569 | } 570 | } 571 | } 572 | 573 | // unsort 574 | STBRP_SORT(rects, num_rects, sizeof(stbrp_rect), rect_original_order); 575 | 576 | // set was_packed flags and all_rects_packed status 577 | for (i=0; i < num_rects; ++i) { 578 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 579 | if (!rects[i].was_packed) 580 | all_rects_packed = 0; 581 | } 582 | 583 | // return the all_rects_packed status 584 | return all_rects_packed; 585 | } 586 | #endif 587 | 588 | /* 589 | ------------------------------------------------------------------------------ 590 | This software is available under 2 licenses -- choose whichever you prefer. 591 | ------------------------------------------------------------------------------ 592 | ALTERNATIVE A - MIT License 593 | Copyright (c) 2017 Sean Barrett 594 | Permission is hereby granted, free of charge, to any person obtaining a copy of 595 | this software and associated documentation files (the "Software"), to deal in 596 | the Software without restriction, including without limitation the rights to 597 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 598 | of the Software, and to permit persons to whom the Software is furnished to do 599 | so, subject to the following conditions: 600 | The above copyright notice and this permission notice shall be included in all 601 | copies or substantial portions of the Software. 602 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 603 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 604 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 605 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 606 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 607 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 608 | SOFTWARE. 609 | ------------------------------------------------------------------------------ 610 | ALTERNATIVE B - Public Domain (www.unlicense.org) 611 | This is free and unencumbered software released into the public domain. 612 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 613 | software, either in source code form or as a compiled binary, for any purpose, 614 | commercial or non-commercial, and by any means. 615 | In jurisdictions that recognize copyright laws, the author or authors of this 616 | software dedicate any and all copyright interest in the software to the public 617 | domain. We make this dedication for the benefit of the public at large and to 618 | the detriment of our heirs and successors. We intend this dedication to be an 619 | overt act of relinquishment in perpetuity of all present and future rights to 620 | this software under copyright law. 621 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 622 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 623 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 624 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 625 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 626 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 627 | ------------------------------------------------------------------------------ 628 | */ 629 | -------------------------------------------------------------------------------- /src/CRuntime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace StbRectPackSharp 5 | { 6 | internal static unsafe class CRuntime 7 | { 8 | public static void* malloc(ulong size) 9 | { 10 | return malloc((long)size); 11 | } 12 | 13 | public static void* malloc(long size) 14 | { 15 | var ptr = Marshal.AllocHGlobal((int)size); 16 | 17 | return ptr.ToPointer(); 18 | } 19 | 20 | public static void free(void* a) 21 | { 22 | var ptr = new IntPtr(a); 23 | Marshal.FreeHGlobal(ptr); 24 | } 25 | 26 | public delegate int QSortComparer(void* a, void* b); 27 | 28 | private static void qsortSwap(byte* data, long size, long pos1, long pos2) 29 | { 30 | var a = data + size * pos1; 31 | var b = data + size * pos2; 32 | 33 | for (long k = 0; k < size; ++k) 34 | { 35 | var tmp = *a; 36 | *a = *b; 37 | *b = tmp; 38 | 39 | a++; 40 | b++; 41 | } 42 | } 43 | 44 | private static long qsortPartition(byte* data, long size, QSortComparer comparer, long left, long right) 45 | { 46 | void* pivot = data + size * left; 47 | var i = left - 1; 48 | var j = right + 1; 49 | for (; ; ) 50 | { 51 | do 52 | { 53 | ++i; 54 | } while (comparer(data + size * i, pivot) < 0); 55 | 56 | do 57 | { 58 | --j; 59 | } while (comparer(data + size * j, pivot) > 0); 60 | 61 | if (i >= j) 62 | { 63 | return j; 64 | } 65 | 66 | qsortSwap(data, size, i, j); 67 | } 68 | } 69 | 70 | 71 | private static void qsortInternal(byte* data, long size, QSortComparer comparer, long left, long right) 72 | { 73 | if (left < right) 74 | { 75 | var p = qsortPartition(data, size, comparer, left, right); 76 | 77 | qsortInternal(data, size, comparer, left, p); 78 | qsortInternal(data, size, comparer, p + 1, right); 79 | } 80 | } 81 | 82 | public static void qsort(void* data, ulong count, ulong size, QSortComparer comparer) 83 | { 84 | qsortInternal((byte*)data, (long)size, comparer, 0, (long)count - 1); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/Packer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using static StbRectPackSharp.StbRectPack; 5 | 6 | namespace StbRectPackSharp 7 | { 8 | #if !STBSHARP_INTERNAL 9 | public 10 | #else 11 | internal 12 | #endif 13 | class PackerRectangle 14 | { 15 | public Rectangle Rectangle { get; private set; } 16 | 17 | public int X => Rectangle.X; 18 | public int Y => Rectangle.Y; 19 | public int Width => Rectangle.Width; 20 | public int Height => Rectangle.Height; 21 | 22 | public object Data { get; private set; } 23 | 24 | public PackerRectangle(Rectangle rect, object data) 25 | { 26 | Rectangle = rect; 27 | Data = data; 28 | } 29 | } 30 | 31 | /// 32 | /// Simple Packer class that doubles size of the atlas if the place runs out 33 | /// 34 | #if !STBSHARP_INTERNAL 35 | public 36 | #else 37 | internal 38 | #endif 39 | unsafe class Packer : IDisposable 40 | { 41 | private readonly stbrp_context _context; 42 | private readonly List _rectangles = new List(); 43 | 44 | public int Width => _context.width; 45 | public int Height => _context.height; 46 | 47 | public List PackRectangles => _rectangles; 48 | 49 | 50 | public Packer(int width = 256, int height = 256) 51 | { 52 | if (width <= 0) 53 | { 54 | throw new ArgumentOutOfRangeException(nameof(width)); 55 | } 56 | 57 | if (height <= 0) 58 | { 59 | throw new ArgumentOutOfRangeException(nameof(height)); 60 | } 61 | 62 | // Initialize the context 63 | var num_nodes = width; 64 | _context = new stbrp_context(num_nodes); 65 | 66 | fixed (stbrp_context* contextPtr = &_context) 67 | { 68 | stbrp_init_target(contextPtr, width, height, _context.all_nodes, num_nodes); 69 | } 70 | } 71 | 72 | public void Dispose() 73 | { 74 | _context.Dispose(); 75 | } 76 | 77 | /// 78 | /// Packs a rect. Returns null, if there's no more place left. 79 | /// 80 | /// 81 | /// 82 | /// 83 | /// 84 | public PackerRectangle PackRect(int width, int height, object userData) 85 | { 86 | var rect = new stbrp_rect 87 | { 88 | id = _rectangles.Count, 89 | w = width, 90 | h = height 91 | }; 92 | 93 | int result; 94 | fixed (stbrp_context* contextPtr = &_context) 95 | { 96 | result = stbrp_pack_rects(contextPtr, &rect, 1); 97 | } 98 | 99 | if (result == 0) 100 | { 101 | return null; 102 | } 103 | 104 | var packRectangle = new PackerRectangle(new Rectangle(rect.x, rect.y, rect.w, rect.h), userData); 105 | _rectangles.Add(packRectangle); 106 | 107 | return packRectangle; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/StbRectPack.Generated.cs: -------------------------------------------------------------------------------- 1 | // Generated by Sichem at 2/18/2021 7:29:26 PM 2 | 3 | using System; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace StbRectPackSharp 7 | { 8 | unsafe partial class StbRectPack 9 | { 10 | public const int STBRP_HEURISTIC_Skyline_default = 0; 11 | public const int STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default; 12 | public const int STBRP_HEURISTIC_Skyline_BF_sortHeight = 2; 13 | public const int STBRP__INIT_skyline = 1; 14 | 15 | [StructLayout(LayoutKind.Sequential)] 16 | public struct stbrp_rect 17 | { 18 | public int id; 19 | public int w; 20 | public int h; 21 | public int x; 22 | public int y; 23 | public int was_packed; 24 | } 25 | 26 | [StructLayout(LayoutKind.Sequential)] 27 | public struct stbrp_node 28 | { 29 | public int x; 30 | public int y; 31 | public stbrp_node* next; 32 | } 33 | 34 | [StructLayout(LayoutKind.Sequential)] 35 | public struct stbrp__findresult 36 | { 37 | public int x; 38 | public int y; 39 | public stbrp_node** prev_link; 40 | } 41 | 42 | public static void stbrp_setup_heuristic(stbrp_context* context, int heuristic) 43 | { 44 | switch (context->init_mode) 45 | { 46 | case STBRP__INIT_skyline: 47 | ; 48 | context->heuristic = heuristic; 49 | break; 50 | default: 51 | throw new Exception("Mode " + context->init_mode + " is not supported."); 52 | } 53 | } 54 | 55 | public static void stbrp_setup_allow_out_of_mem(stbrp_context* context, int allow_out_of_mem) 56 | { 57 | if (allow_out_of_mem != 0) 58 | context->align = 1; 59 | else 60 | context->align = (context->width + context->num_nodes - 1) / context->num_nodes; 61 | } 62 | 63 | public static void stbrp_init_target(stbrp_context* context, int width, int height, stbrp_node* nodes, 64 | int num_nodes) 65 | { 66 | var i = 0; 67 | for (i = 0; i < num_nodes - 1; ++i) 68 | nodes[i].next = &nodes[i + 1]; 69 | nodes[i].next = null; 70 | context->init_mode = STBRP__INIT_skyline; 71 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 72 | context->free_head = &nodes[0]; 73 | context->active_head = &context->extra[0]; 74 | context->width = width; 75 | context->height = height; 76 | context->num_nodes = num_nodes; 77 | stbrp_setup_allow_out_of_mem(context, 0); 78 | context->extra[0].x = 0; 79 | context->extra[0].y = 0; 80 | context->extra[0].next = &context->extra[1]; 81 | context->extra[1].x = (int)width; 82 | context->extra[1].y = 65535; 83 | context->extra[1].next = null; 84 | } 85 | 86 | public static int stbrp__skyline_find_min_y(stbrp_context* c, stbrp_node* first, int x0, int width, int* pwaste) 87 | { 88 | var node = first; 89 | var x1 = x0 + width; 90 | var min_y = 0; 91 | var visited_width = 0; 92 | var waste_area = 0; 93 | min_y = 0; 94 | waste_area = 0; 95 | visited_width = 0; 96 | while (node->x < x1) 97 | { 98 | if (node->y > min_y) 99 | { 100 | waste_area += visited_width * (node->y - min_y); 101 | min_y = node->y; 102 | if (node->x < x0) 103 | visited_width += node->next->x - x0; 104 | else 105 | visited_width += node->next->x - node->x; 106 | } 107 | else 108 | { 109 | var under_width = node->next->x - node->x; 110 | if (under_width + visited_width > width) 111 | under_width = width - visited_width; 112 | waste_area += under_width * (min_y - node->y); 113 | visited_width += under_width; 114 | } 115 | 116 | node = node->next; 117 | } 118 | 119 | *pwaste = waste_area; 120 | return min_y; 121 | } 122 | 123 | public static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context* c, int width, int height) 124 | { 125 | var best_waste = 1 << 30; 126 | var best_x = 0; 127 | var best_y = 1 << 30; 128 | var fr = new stbrp__findresult(); 129 | stbrp_node** prev; 130 | stbrp_node* node; 131 | stbrp_node* tail; 132 | stbrp_node** best = null; 133 | width = width + c->align - 1; 134 | width -= width % c->align; 135 | if (width > c->width || height > c->height) 136 | { 137 | fr.prev_link = null; 138 | fr.x = fr.y = 0; 139 | return fr; 140 | } 141 | 142 | node = c->active_head; 143 | prev = &c->active_head; 144 | while (node->x + width <= c->width) 145 | { 146 | var y = 0; 147 | var waste = 0; 148 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 149 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) 150 | { 151 | if (y < best_y) 152 | { 153 | best_y = y; 154 | best = prev; 155 | } 156 | } 157 | else 158 | { 159 | if (y + height <= c->height) 160 | if (y < best_y || y == best_y && waste < best_waste) 161 | { 162 | best_y = y; 163 | best_waste = waste; 164 | best = prev; 165 | } 166 | } 167 | 168 | prev = &node->next; 169 | node = node->next; 170 | } 171 | 172 | best_x = best == null ? 0 : (*best)->x; 173 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) 174 | { 175 | tail = c->active_head; 176 | node = c->active_head; 177 | prev = &c->active_head; 178 | while (tail->x < width) 179 | tail = tail->next; 180 | while (tail != null) 181 | { 182 | var xpos = tail->x - width; 183 | var y = 0; 184 | var waste = 0; 185 | while (node->next->x <= xpos) 186 | { 187 | prev = &node->next; 188 | node = node->next; 189 | } 190 | 191 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 192 | if (y + height <= c->height) 193 | if (y <= best_y) 194 | if (y < best_y || waste < best_waste || waste == best_waste && xpos < best_x) 195 | { 196 | best_x = xpos; 197 | best_y = y; 198 | best_waste = waste; 199 | best = prev; 200 | } 201 | 202 | tail = tail->next; 203 | } 204 | } 205 | 206 | fr.prev_link = best; 207 | fr.x = best_x; 208 | fr.y = best_y; 209 | return fr; 210 | } 211 | 212 | public static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context* context, int width, int height) 213 | { 214 | var res = stbrp__skyline_find_best_pos(context, width, height); 215 | stbrp_node* node; 216 | stbrp_node* cur; 217 | if (res.prev_link == null || res.y + height > context->height || context->free_head == null) 218 | { 219 | res.prev_link = null; 220 | return res; 221 | } 222 | 223 | node = context->free_head; 224 | node->x = (int)res.x; 225 | node->y = (int)(res.y + height); 226 | context->free_head = node->next; 227 | cur = *res.prev_link; 228 | if (cur->x < res.x) 229 | { 230 | var next = cur->next; 231 | cur->next = node; 232 | cur = next; 233 | } 234 | else 235 | { 236 | *res.prev_link = node; 237 | } 238 | 239 | while (cur->next != null && cur->next->x <= res.x + width) 240 | { 241 | var next = cur->next; 242 | cur->next = context->free_head; 243 | context->free_head = cur; 244 | cur = next; 245 | } 246 | 247 | node->next = cur; 248 | if (cur->x < res.x + width) 249 | cur->x = (int)(res.x + width); 250 | return res; 251 | } 252 | 253 | public static int rect_height_compare(void* a, void* b) 254 | { 255 | var p = (stbrp_rect*)a; 256 | var q = (stbrp_rect*)b; 257 | if (p->h > q->h) 258 | return -1; 259 | if (p->h < q->h) 260 | return 1; 261 | return p->w > q->w ? -1 : p->w < q->w ? 1 : 0; 262 | } 263 | 264 | public static int rect_original_order(void* a, void* b) 265 | { 266 | var p = (stbrp_rect*)a; 267 | var q = (stbrp_rect*)b; 268 | return p->was_packed < q->was_packed ? -1 : p->was_packed > q->was_packed ? 1 : 0; 269 | } 270 | 271 | public static int stbrp_pack_rects(stbrp_context* context, stbrp_rect* rects, int num_rects) 272 | { 273 | var i = 0; 274 | var all_rects_packed = 1; 275 | for (i = 0; i < num_rects; ++i) 276 | rects[i].was_packed = i; 277 | CRuntime.qsort(rects, (ulong)num_rects, (ulong)sizeof(stbrp_rect), rect_height_compare); 278 | for (i = 0; i < num_rects; ++i) 279 | if (rects[i].w == 0 || rects[i].h == 0) 280 | { 281 | rects[i].x = rects[i].y = 0; 282 | } 283 | else 284 | { 285 | var fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 286 | if (fr.prev_link != null) 287 | { 288 | rects[i].x = (int)fr.x; 289 | rects[i].y = (int)fr.y; 290 | } 291 | else 292 | { 293 | rects[i].x = rects[i].y = 0xffff; 294 | } 295 | } 296 | 297 | CRuntime.qsort(rects, (ulong)num_rects, (ulong)sizeof(stbrp_rect), rect_original_order); 298 | for (i = 0; i < num_rects; ++i) 299 | { 300 | rects[i].was_packed = rects[i].x == 0xffff && rects[i].y == 0xffff ? 0 : 1; 301 | if (rects[i].was_packed == 0) 302 | all_rects_packed = 0; 303 | } 304 | 305 | return all_rects_packed; 306 | } 307 | } 308 | } -------------------------------------------------------------------------------- /src/StbRectPack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbRectPackSharp 4 | { 5 | #if !STBSHARP_INTERNAL 6 | public 7 | #else 8 | internal 9 | #endif 10 | static unsafe partial class StbRectPack 11 | { 12 | public struct stbrp_context: IDisposable 13 | { 14 | public int width; 15 | public int height; 16 | public int align; 17 | public int init_mode; 18 | public int heuristic; 19 | public int num_nodes; 20 | public stbrp_node* active_head; 21 | public stbrp_node* free_head; 22 | public stbrp_node* extra; 23 | public stbrp_node* all_nodes; 24 | 25 | 26 | public stbrp_context(int nodesCount) 27 | { 28 | if (nodesCount <= 0) 29 | { 30 | throw new ArgumentOutOfRangeException(nameof(nodesCount)); 31 | } 32 | 33 | width = height = align = init_mode = heuristic = num_nodes = 0; 34 | active_head = free_head = null; 35 | 36 | // Allocate nodes 37 | all_nodes = (stbrp_node*)CRuntime.malloc(sizeof(stbrp_node) * nodesCount); 38 | 39 | // Allocate extras 40 | extra = (stbrp_node*)CRuntime.malloc(sizeof(stbrp_node) * 2); 41 | } 42 | 43 | public void Dispose() 44 | { 45 | if (all_nodes != null) 46 | { 47 | CRuntime.free(all_nodes); 48 | all_nodes = null; 49 | } 50 | 51 | if (extra != null) 52 | { 53 | CRuntime.free(extra); 54 | extra = null; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/StbRectPackSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | StbRectPackSharpTeam 4 | StbRectPackSharp 5 | StbRectPackSharp 6 | netstandard2.0;net45 7 | C# port of the stb_rect_pack.h 8 | https://github.com/StbSharp/StbRectPackSharp 9 | Unlicense 10 | 1.00.4 11 | true 12 | 13 | 14 | 15 | true 16 | 17 | -------------------------------------------------------------------------------- /tests/StbRectPackSharp.Tests/StbRectPackSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/StbRectPackSharp.Tests/Tests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using StbRectPackSharp; 3 | 4 | namespace StbImageSharp.Tests 5 | { 6 | [TestFixture] 7 | public class Tests 8 | { 9 | [Test] 10 | public void SimplePacking() 11 | { 12 | // Pack 3 rects 13 | var packer = new Packer(); 14 | 15 | packer.PackRect(10, 10, "a"); 16 | packer.PackRect(15, 10, "b"); 17 | packer.PackRect(10, 20, "c"); 18 | 19 | Assert.AreEqual(packer.PackRectangles.Count, 3); 20 | 21 | var rect = packer.PackRectangles[0]; 22 | Assert.AreEqual(rect.Rectangle.Width, 10); 23 | Assert.AreEqual(rect.Rectangle.Height, 10); 24 | Assert.AreEqual(rect.Data, "a"); 25 | 26 | rect = packer.PackRectangles[1]; 27 | Assert.AreEqual(rect.Rectangle.Width, 15); 28 | Assert.AreEqual(rect.Rectangle.Height, 10); 29 | Assert.AreEqual(rect.Data, "b"); 30 | 31 | rect = packer.PackRectangles[2]; 32 | Assert.AreEqual(rect.Rectangle.Width, 10); 33 | Assert.AreEqual(rect.Rectangle.Height, 20); 34 | Assert.AreEqual(rect.Data, "c"); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/StbRectPackSharp.VisualizePacking/Fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/StbRectPackSharp/aeab866195d872fd311a1bf6d291fc7d2c79f5df/tests/StbRectPackSharp.VisualizePacking/Fonts/DroidSans.ttf -------------------------------------------------------------------------------- /tests/StbRectPackSharp.VisualizePacking/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbImageSharp.Samples.MonoGame 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 | static void Main(string[] args) 15 | { 16 | try 17 | { 18 | using (var game = new VisualizerGame()) 19 | game.Run(); 20 | } 21 | catch(Exception ex) 22 | { 23 | Console.WriteLine(ex); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/StbRectPackSharp.VisualizePacking/StbRectPackSharp.VisualizePacking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net45 5 | bin\MonoGame\$(Configuration) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/StbRectPackSharp.VisualizePacking/VisualizerGame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FontStashSharp; 4 | using Microsoft.Xna.Framework; 5 | using Microsoft.Xna.Framework.Graphics; 6 | using StbRectPackSharp; 7 | 8 | namespace StbImageSharp.Samples.MonoGame 9 | { 10 | /// 11 | /// This is the main type for your game. 12 | /// 13 | public class VisualizerGame : Game 14 | { 15 | private const int RectangleAddDelayInMs = 100; 16 | private const int MaximumRectangleSize = 50; 17 | private const int BorderThickness = 2; 18 | 19 | private GraphicsDeviceManager _graphics; 20 | private SpriteBatch _spriteBatch; 21 | private Texture2D _white; 22 | private readonly Random _random = new Random(); 23 | private Packer _packer = new Packer(128, 128); 24 | private DateTime? _addedLast; 25 | private FontSystem _fontSystem; 26 | 27 | public VisualizerGame() 28 | { 29 | _graphics = new GraphicsDeviceManager(this) 30 | { 31 | PreferredBackBufferWidth = 1400, 32 | PreferredBackBufferHeight = 960 33 | }; 34 | 35 | Content.RootDirectory = "Content"; 36 | IsMouseVisible = true; 37 | Window.AllowUserResizing = true; 38 | } 39 | 40 | /// 41 | /// LoadContent will be called once per game and is the place to load 42 | /// all of your content. 43 | /// 44 | protected override void LoadContent() 45 | { 46 | // Create a new SpriteBatch, which can be used to draw textures. 47 | _spriteBatch = new SpriteBatch(GraphicsDevice); 48 | 49 | // White texture 50 | _white = new Texture2D(GraphicsDevice, 1, 1); 51 | _white.SetData(new[] { Color.White }); 52 | 53 | // Font 54 | var data = File.ReadAllBytes("Fonts/DroidSans.ttf"); 55 | _fontSystem = FontSystemFactory.Create(GraphicsDevice); 56 | _fontSystem.AddFont(data); 57 | } 58 | 59 | protected override void Update(GameTime gameTime) 60 | { 61 | base.Update(gameTime); 62 | 63 | if (_addedLast == null || (DateTime.Now - _addedLast.Value).TotalMilliseconds >= RectangleAddDelayInMs) 64 | { 65 | var color = new Color(_random.Next(0, 256), _random.Next(0, 256), _random.Next(0, 256)); 66 | var width = _random.Next(1, MaximumRectangleSize); 67 | var height = _random.Next(1, MaximumRectangleSize); 68 | 69 | var pr = _packer.PackRect(width, height, color); 70 | 71 | // Double the size of the packer until the new rectangle will fit 72 | while (pr == null) 73 | { 74 | Packer newPacker = new Packer(_packer.Width * 2, _packer.Height * 2); 75 | 76 | // Place existing rectangles 77 | foreach (PackerRectangle existingRect in _packer.PackRectangles) 78 | { 79 | newPacker.PackRect(existingRect.Width, existingRect.Height, existingRect.Data); 80 | } 81 | 82 | // Now dispose old packer and assign new one 83 | _packer.Dispose(); 84 | _packer = newPacker; 85 | 86 | // Try to fit the rectangle again 87 | pr = _packer.PackRect(width, height, color); 88 | } 89 | 90 | _addedLast = DateTime.Now; 91 | } 92 | } 93 | 94 | /// 95 | /// Draws a rectangle with the thickness provided 96 | /// 97 | /// The destination drawing surface 98 | /// 99 | /// The rectangle to draw 100 | /// The color to draw the rectangle in 101 | /// The thickness of the lines 102 | public static void DrawRectangle(SpriteBatch spriteBatch, Texture2D texture, Rectangle rectangle, Color color, float thickness = 1f) 103 | { 104 | var t = (int)thickness; 105 | 106 | // Top 107 | spriteBatch.Draw(texture, new Rectangle(rectangle.X, rectangle.Y, rectangle.Width, t), null, color); 108 | 109 | // Bottom 110 | spriteBatch.Draw(texture, new Rectangle(rectangle.X, rectangle.Bottom - t, rectangle.Width, t), null, color); 111 | 112 | // Left 113 | spriteBatch.Draw(texture, new Rectangle(rectangle.X, rectangle.Y, t, rectangle.Height), null, color); 114 | 115 | // Right 116 | spriteBatch.Draw(texture, new Rectangle(rectangle.Right - t, rectangle.Y, t, rectangle.Height), null, color); 117 | } 118 | 119 | /// 120 | /// This is called when the game should draw itself. 121 | /// 122 | /// Provides a snapshot of timing values. 123 | protected override void Draw(GameTime gameTime) 124 | { 125 | GraphicsDevice.Clear(Color.CornflowerBlue); 126 | 127 | // TODO: Add your drawing code here 128 | _spriteBatch.Begin(); 129 | 130 | DrawRectangle(_spriteBatch, _white, new Rectangle(0, 0, _packer.Width + BorderThickness * 2, _packer.Height + +BorderThickness * 2), Color.White, BorderThickness); 131 | 132 | 133 | foreach(var rect in _packer.PackRectangles) 134 | { 135 | _spriteBatch.Draw(_white, new Rectangle(rect.X + BorderThickness, rect.Y + BorderThickness, rect.Width, rect.Height), (Color)rect.Data); 136 | } 137 | 138 | var font = _fontSystem.GetFont(32); 139 | 140 | font.DrawText(_spriteBatch, "Rectangles: " + _packer.PackRectangles.Count, new Vector2(BorderThickness, BorderThickness), Color.White); 141 | 142 | var text = _packer.Width + "x" + _packer.Height; 143 | var size = font.MeasureString(text); 144 | 145 | font.DrawText(_spriteBatch, text, new Vector2((_packer.Width - size.X) / 2 + BorderThickness, (_packer.Height - size.Y) / 2 + BorderThickness), Color.White); 146 | 147 | _spriteBatch.End(); 148 | 149 | base.Draw(gameTime); 150 | } 151 | } 152 | } --------------------------------------------------------------------------------