├── .editorconfig ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── ask-question.md │ ├── bug-report.md │ └── feature-request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── SixLabors.Core.sln ├── SixLabors.Core.sln.DotSettings ├── appveyor.yml ├── build.cmd ├── build.ps1 ├── codecov.yml ├── gitversion.yml ├── src └── SixLabors.Core │ ├── Constants.cs │ ├── GeometryUtilities.cs │ ├── Memory │ ├── AllocationOptions.cs │ ├── ArrayPoolMemoryAllocator.Buffer{T}.cs │ ├── ArrayPoolMemoryAllocator.CommonFactoryMethods.cs │ ├── ArrayPoolMemoryAllocator.cs │ ├── IManagedByteBuffer.cs │ ├── Internals │ │ ├── BasicArrayBuffer.cs │ │ ├── BasicByteBuffer.cs │ │ └── ManagedBufferBase.cs │ ├── MemoryAllocator.cs │ └── SimpleGcMemoryAllocator.cs │ ├── Primitives │ ├── Matrix3x2Extensions.cs │ ├── Point.cs │ ├── PointF.cs │ ├── Rectangle.cs │ ├── RectangleF.cs │ ├── Size.cs │ └── SizeF.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SixLabors.Core.csproj │ └── SixLabors.Core.csproj.DotSettings └── tests ├── CodeCoverage ├── .gitignore ├── CodeCoverage.cmd └── packages.config ├── SixLabors.Core.Tests ├── Helpers │ ├── DebugGuardTests.cs │ ├── FloatRoundingComparer.cs │ ├── GeometryUtilitiesTests.cs │ ├── GuardTests.cs │ └── MathFTests.cs ├── Memory │ ├── ArrayPoolMemoryAllocatorTests.cs │ ├── BufferExtensions.cs │ ├── BufferTestSuite.cs │ └── SimpleGcMemoryAllocatorTests.cs ├── Primitives │ ├── PointFTests.cs │ ├── PointTests.cs │ ├── RectangleFTests.cs │ ├── RectangleTests.cs │ ├── SizeFTests.cs │ └── SizeTests.cs ├── SixLabors.Core.Tests.csproj └── TestEnvironment.cs └── SixLabors.ruleset /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to: 3 | # treat as text and 4 | # normalize to Unix-style line endings 5 | ############################################################################### 6 | * text eol=lf 7 | 8 | ############################################################################### 9 | # Set explicit file behavior to: 10 | # treat as text and 11 | # normalize to Unix-style line endings 12 | ############################################################################### 13 | *.asm text eol=lf 14 | *.c text eol=lf 15 | *.clj text eol=lf 16 | *.cmd text eol=lf 17 | *.cpp text eol=lf 18 | *.css text eol=lf 19 | *.cxx text eol=lf 20 | *.config text eol=lf 21 | *.DotSettings text eol=lf 22 | *.erl text eol=lf 23 | *.fs text eol=lf 24 | *.fsx text eol=lf 25 | *.h text eol=lf 26 | *.htm text eol=lf 27 | *.html text eol=lf 28 | *.hs text eol=lf 29 | *.hxx text eol=lf 30 | *.java text eol=lf 31 | *.js text eol=lf 32 | *.json text eol=lf 33 | *.less text eol=lf 34 | *.lisp text eol=lf 35 | *.lua text eol=lf 36 | *.m text eol=lf 37 | *.md text eol=lf 38 | *.php text eol=lf 39 | *.props text eol=lf 40 | *.ps1 text eol=lf 41 | *.py text eol=lf 42 | *.rb text eol=lf 43 | *.resx text eol=lf 44 | *.runsettings text eol=lf 45 | *.ruleset text eol=lf 46 | *.sass text eol=lf 47 | *.scss text eol=lf 48 | *.sh text eol=lf 49 | *.sql text eol=lf 50 | *.svg text eol=lf 51 | *.targets text eol=lf 52 | *.tt text eol=crlf 53 | *.ttinclude text eol=crlf 54 | *.txt text eol=lf 55 | *.vb text eol=lf 56 | *.yml text eol=lf 57 | 58 | ############################################################################### 59 | # Set explicit file behavior to: 60 | # treat as text 61 | # normalize to Unix-style line endings and 62 | # diff as csharp 63 | ############################################################################### 64 | *.cs text eol=lf diff=csharp 65 | 66 | ############################################################################### 67 | # Set explicit file behavior to: 68 | # treat as text 69 | # normalize to Unix-style line endings and 70 | # use a union merge when resoling conflicts 71 | ############################################################################### 72 | *.csproj text eol=lf merge=union 73 | *.dbproj text eol=lf merge=union 74 | *.fsproj text eol=lf merge=union 75 | *.ncrunchproject text eol=lf merge=union 76 | *.vbproj text eol=lf merge=union 77 | 78 | ############################################################################### 79 | # Set explicit file behavior to: 80 | # treat as text 81 | # normalize to Windows-style line endings and 82 | # use a union merge when resoling conflicts 83 | ############################################################################### 84 | *.sln text eol=crlf merge=union 85 | 86 | ############################################################################### 87 | # Set explicit file behavior to: 88 | # treat as binary 89 | ############################################################################### 90 | *.bmp binary 91 | *.dll binary 92 | *.exe binary 93 | *.gif binary 94 | *.jpg binary 95 | *.png binary 96 | *.ttf binary 97 | *.snk binary 98 | 99 | ############################################################################### 100 | # Set explicit file behavior to: 101 | # diff as plain text 102 | ############################################################################### 103 | *.doc diff=astextplain 104 | *.docx diff=astextplain 105 | *.dot diff=astextplain 106 | *.pdf diff=astextplain 107 | *.pptx diff=astextplain 108 | *.rtf diff=astextplain 109 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to SixLabors.Core 2 | 3 | #### **Did you find a bug?** 4 | 5 | - Please **ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/SixLabors/Core/issues). 6 | 7 | - If you're unable to find an open issue addressing the problem, please [open a new one](https://github.com/SixLabors/Core/issues/new). Be sure to include a **title, the applicable version, a clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. Please do not hijack existing issues. 8 | 9 | #### **Did you write a patch that fixes a bug?** 10 | 11 | * Open a new GitHub pull request with the patch. 12 | 13 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 14 | 15 | * Before submitting, please ensure that your code matches the existing coding patterns and practise as demonstrated in the repository. These follow strict Stylecop rules :cop:. 16 | 17 | #### **Do you intend to add a new feature or change an existing one?** 18 | 19 | * Suggest your change in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General) and start writing code. 20 | 21 | * Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes. 22 | 23 | #### **Running tests and Debugging** 24 | 25 | * Debugging (running tests in Debug mode) is only supported on .NET Core 2.1, because of JIT Code Generation bugs like [dotnet/coreclr#16443](https://github.com/dotnet/coreclr/issues/16443) or [dotnet/coreclr#20657](https://github.com/dotnet/coreclr/issues/20657) 26 | 27 | #### **Do you have questions about consuming the library or the source code?** 28 | 29 | * Ask any question about how to use SixLabors.Core in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General). 30 | 31 | And please remember. SixLabors.Core is the work of a very, very, small number of developers who struggle balancing time to contribute to the project with family time and work commitments. We encourage you to pitch in and help make our vision of simple accessible imageprocessing available to all. Open Source can only exist with your help. 32 | 33 | Thanks for reading! 34 | 35 | James Jackson-South :heart: 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ask-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask question 3 | about: Ask a question about this project. 4 | 5 | --- 6 | 7 | You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General 8 | 9 | You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General 10 | 11 | You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General 12 | 13 | You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ### Prerequisites 8 | 9 | - [ ] I have written a descriptive issue title 10 | - [ ] I have verified that I am running the latest version of ImageSharp 11 | - [ ] I have verified if the problem exist in both `DEBUG` and `RELEASE` mode 12 | - [ ] I have searched [open](https://github.com/SixLabors/Core/issues) and [closed](https://github.com/SixLabors/Core/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported 13 | 14 | ### Description 15 | 16 | 17 | ### Steps to Reproduce 18 | 19 | 20 | ### System Configuration 21 | 22 | 23 | - SixLabors.Core version: 24 | - Other SixLabors packages and versions: 25 | - Environment (Operating system, version and so on): 26 | - .NET Framework version: 27 | - Additional information: 28 | 29 | 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General 8 | 9 | You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General 10 | 11 | You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General 12 | 13 | You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Prerequisites 2 | 3 | - [ ] I have written a descriptive pull-request title 4 | - [ ] I have verified that there are no overlapping [pull-requests](https://github.com/SixLabors/Core/pulls) open 5 | - [ ] I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules :cop:. 6 | - [ ] I have provided test coverage for my change (where applicable) 7 | 8 | ### Description 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "shared-infrastructure"] 2 | path = shared-infrastructure 3 | url = https://github.com/SixLabors/SharedInfrastructure.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The contents of this repository have been integrated into https://github.com/SixLabors/ImageSharp 2 | -------------------------------------------------------------------------------- /SixLabors.Core.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.352 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" 7 | ProjectSection(SolutionItems) = preProject 8 | appveyor.yml = appveyor.yml 9 | .github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md 10 | .github\ISSUE_TEMPLATE\bug-report.md = .github\ISSUE_TEMPLATE\bug-report.md 11 | build.cmd = build.cmd 12 | codecov.yml = codecov.yml 13 | .github\CONTRIBUTING.md = .github\CONTRIBUTING.md 14 | .github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md 15 | gitversion.yml = gitversion.yml 16 | .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md 17 | README.md = README.md 18 | shared-infrastructure\.editorconfig = shared-infrastructure\.editorconfig 19 | EndProjectSection 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" 22 | EndProject 23 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SixLabors.Core", "src\SixLabors.Core\SixLabors.Core.csproj", "{09E744EC-4852-4FC7-BE78-C1B399F17967}" 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SixLabors.Core.Tests", "tests\SixLabors.Core.Tests\SixLabors.Core.Tests.csproj", "{F836E8E6-B4D9-4208-8346-140C74678B91}" 28 | EndProject 29 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeCoverage", "CodeCoverage", "{10A74B46-930F-49E3-A579-BC3A6A23321D}" 30 | ProjectSection(SolutionItems) = preProject 31 | tests\CodeCoverage\CodeCoverage.cmd = tests\CodeCoverage\CodeCoverage.cmd 32 | EndProjectSection 33 | EndProject 34 | Global 35 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 36 | Debug|Any CPU = Debug|Any CPU 37 | Release|Any CPU = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 40 | {09E744EC-4852-4FC7-BE78-C1B399F17967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {09E744EC-4852-4FC7-BE78-C1B399F17967}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {09E744EC-4852-4FC7-BE78-C1B399F17967}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {09E744EC-4852-4FC7-BE78-C1B399F17967}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {F836E8E6-B4D9-4208-8346-140C74678B91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {F836E8E6-B4D9-4208-8346-140C74678B91}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {F836E8E6-B4D9-4208-8346-140C74678B91}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {F836E8E6-B4D9-4208-8346-140C74678B91}.Release|Any CPU.Build.0 = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(NestedProjects) = preSolution 53 | {09E744EC-4852-4FC7-BE78-C1B399F17967} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} 54 | {F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} 55 | {10A74B46-930F-49E3-A579-BC3A6A23321D} = {C317F1B1-D75E-4C6D-83EB-80367343E0D7} 56 | EndGlobalSection 57 | GlobalSection(ExtensibilityGlobals) = postSolution 58 | SolutionGuid = {0DED1AC8-37DA-4EC2-8CAE-40E31CD439DE} 59 | EndGlobalSection 60 | GlobalSection(Performance) = preSolution 61 | HasPerformanceSessions = true 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /SixLabors.Core.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.0.{build} 2 | image: Visual Studio 2017 3 | 4 | 5 | before_build: 6 | - git submodule -q update --init 7 | - cmd: dotnet --version 8 | 9 | build_script: 10 | - cmd: build.cmd 11 | - cmd: tests\CodeCoverage\CodeCoverage.cmd 12 | 13 | after_build: 14 | - cmd: appveyor PushArtifact "artifacts\SixLabors.Core.%APPVEYOR_BUILD_VERSION%.nupkg" 15 | 16 | deploy: 17 | - provider: NuGet 18 | server: https://www.myget.org/F/sixlabors/api/v2/package 19 | symbol_server: https://www.myget.org/F/sixlabors/symbols/api/v2/package 20 | api_key: 21 | secure: V/lEHP0UeMWIpWd0fiNlY2IgbCnJKQlGdRksECdJbOBdaE20Fl0RNL7WyqHe02o4 22 | artifact: /.*\.nupkg/ 23 | on: 24 | branch: master 25 | 26 | # prevent the double build when a branch has an active PR 27 | skip_branch_with_pr: true 28 | 29 | test: off 30 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo Off 2 | 3 | PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '.\build.ps1'" 4 | 5 | if not "%errorlevel%"=="0" goto failure 6 | 7 | :success 8 | ECHO successfully built project 9 | REM exit 0 10 | goto end 11 | 12 | :failure 13 | ECHO failed to build. 14 | REM exit -1 15 | goto end 16 | 17 | :end -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | 2 | # lets calulat the correct version here 3 | $fallbackVersion = "1.0.0"; 4 | $version = '' 5 | 6 | $tagRegex = '^v?(\d+\.\d+\.\d+)(-([a-zA-Z]+)\.?(\d*))?$' 7 | 8 | # we are running on the build server 9 | $isVersionTag = $env:APPVEYOR_REPO_TAG_NAME -match $tagRegex 10 | 11 | if($isVersionTag){ 12 | 13 | Write-Debug "Building commit tagged with a compatable version number" 14 | 15 | $version = $matches[1] 16 | $postTag = $matches[3] 17 | $count = $matches[4] 18 | Write-Debug "version number: ${version} post tag: ${postTag} count: ${count}" 19 | if("$postTag" -ne ""){ 20 | $version = "${version}-${postTag}" 21 | } 22 | if("$count" -ne ""){ 23 | # for consistancy with previous releases we pad the counter to only 4 places 24 | $padded = $count.Trim().Trim('0').PadLeft(4,"0"); 25 | Write-Debug "count '$count', padded '${padded}'" 26 | 27 | $version = "${version}${padded}" 28 | } 29 | }else { 30 | 31 | Write-Debug "Untagged" 32 | $lastTag = (git tag --list --sort=-taggerdate) | Out-String 33 | $list = $lastTag.Split("`n") 34 | foreach ($tag in $list) { 35 | 36 | Write-Debug "testing ${tag}" 37 | $tag = $tag.Trim(); 38 | if($tag -match $tagRegex){ 39 | Write-Debug "matched ${tag}" 40 | $version = $matches[1]; 41 | break; 42 | } 43 | } 44 | 45 | if("$version" -eq ""){ 46 | $version = $fallbackVersion 47 | Write-Debug "Failed to discover base version Fallback to '${version}'" 48 | }else{ 49 | 50 | Write-Debug "Discovered base version from tags '${version}'" 51 | } 52 | 53 | $buildNumber = $env:APPVEYOR_BUILD_NUMBER 54 | 55 | # build number replacement is padded to 6 places 56 | $buildNumber = "$buildNumber".Trim().Trim('0').PadLeft(6,"0"); 57 | if("$env:APPVEYOR_PULL_REQUEST_NUMBER" -ne ""){ 58 | Write-Debug "building a PR" 59 | 60 | $prNumber = "$env:APPVEYOR_PULL_REQUEST_NUMBER".Trim().Trim('0').PadLeft(5,"0"); 61 | # this is a PR 62 | $version = "${version}-PullRequest${prNumber}${buildNumber}"; 63 | }else{ 64 | Write-Debug "building a branch commit" 65 | 66 | # this is a general branch commit 67 | $branch = $env:APPVEYOR_REPO_BRANCH 68 | 69 | if("$branch" -eq ""){ 70 | $branch = ((git rev-parse --abbrev-ref HEAD) | Out-String).Trim() 71 | 72 | if("$branch" -eq ""){ 73 | $branch = "unknown" 74 | } 75 | } 76 | 77 | $branch = $branch.Replace("/","-").ToLower() 78 | 79 | if($branch.ToLower() -eq "master"){ 80 | $branch = "dev" 81 | } 82 | 83 | $version = "${version}-${branch}${buildNumber}"; 84 | } 85 | } 86 | 87 | if("$env:APPVEYOR_API_URL" -ne ""){ 88 | # update appveyor build number for this build 89 | Invoke-RestMethod -Method "PUT" ` 90 | -Uri "${env:APPVEYOR_API_URL}api/build" ` 91 | -Body "{version:'${version}'}" ` 92 | -ContentType "application/json" 93 | } 94 | 95 | Write-Host "Building version '${version}'" 96 | dotnet restore /p:packageversion=$version 97 | 98 | Write-Host "Building projects" 99 | dotnet build -c Release /p:packageversion=$version 100 | 101 | if ($LASTEXITCODE ){ Exit $LASTEXITCODE } 102 | 103 | if ( $env:CI -ne "True") { 104 | dotnet test ./tests/SixLabors.Core.Tests/SixLabors.Core.Tests.csproj --no-build -c Release 105 | } 106 | if ($LASTEXITCODE ){ Exit $LASTEXITCODE } 107 | 108 | Write-Host "Packaging projects" 109 | dotnet pack ./src/SixLabors.Core/ -c Release --output ../../artifacts --no-build /p:packageversion=$version 110 | if ($LASTEXITCODE ){ Exit $LASTEXITCODE } -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: develop 3 | notify: 4 | require_ci_to_pass: true 5 | comment: off 6 | coverage: 7 | precision: 2 8 | range: 9 | - 70.0 10 | - 100.0 11 | round: down 12 | status: 13 | changes: false 14 | patch: true 15 | project: true 16 | parsers: 17 | gcov: 18 | branch_detection: 19 | conditional: true 20 | loop: true 21 | macro: false 22 | method: false -------------------------------------------------------------------------------- /gitversion.yml: -------------------------------------------------------------------------------- 1 | # to create a new package you create a new release/tag 2 | # in github appveyor will build it without the -cixxx tag 3 | # it will then be deployable cleanly to nuget.org 4 | 5 | branches: 6 | master: 7 | tag: ci 8 | mode: ContinuousDeployment 9 | increment: Minor 10 | prevent-increment-of-merged-branch-version: false 11 | track-merge-target: true 12 | pull-request: 13 | regex: (pull|pull\-requests|pr)[/-] 14 | mode: ContinuousDelivery 15 | tag: PullRequest 16 | increment: Inherit 17 | prevent-increment-of-merged-branch-version: false 18 | tag-number-pattern: '[/-](?\d+)[-/]' 19 | track-merge-target: false 20 | tracks-release-branches: false 21 | is-release-branch: false 22 | otherbranches: 23 | regex: '.*' 24 | mode: ContinuousDeployment 25 | tag: ci 26 | increment: Patch 27 | prevent-increment-of-merged-branch-version: false 28 | track-merge-target: true 29 | is-release-branch: false 30 | ignore: 31 | sha: [] -------------------------------------------------------------------------------- /src/SixLabors.Core/Constants.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | namespace SixLabors 5 | { 6 | /// 7 | /// Common constants used throughout the project. 8 | /// 9 | internal static class Constants 10 | { 11 | /// 12 | /// The epsilon for comparing floating point numbers. 13 | /// 14 | public static readonly float Epsilon = 0.001f; 15 | } 16 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/GeometryUtilities.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace SixLabors 8 | { 9 | /// 10 | /// Utility class for common geometric functions. 11 | /// 12 | public static class GeometryUtilities 13 | { 14 | /// 15 | /// Converts a degree (360-periodic) angle to a radian (2*Pi-periodic) angle. 16 | /// 17 | /// The angle in degrees. 18 | /// 19 | /// The representing the degree as radians. 20 | /// 21 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 22 | public static float DegreeToRadian(float degree) => degree * (MathF.PI / 180F); 23 | 24 | /// 25 | /// Converts a radian (2*Pi-periodic) angle to a degree (360-periodic) angle. 26 | /// 27 | /// The angle in radians. 28 | /// 29 | /// The representing the degree as radians. 30 | /// 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public static float RadianToDegree(float radian) => radian / (MathF.PI / 180F); 33 | } 34 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/AllocationOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | namespace SixLabors.Memory 5 | { 6 | /// 7 | /// Options for allocating buffers. 8 | /// 9 | public enum AllocationOptions 10 | { 11 | /// 12 | /// Indicates that the buffer should just be allocated. 13 | /// 14 | None, 15 | 16 | /// 17 | /// Indicates that the allocated buffer should be cleaned following allocation. 18 | /// 19 | Clean 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Buffers; 6 | using System.Runtime.InteropServices; 7 | using SixLabors.Memory.Internals; 8 | 9 | namespace SixLabors.Memory 10 | { 11 | /// 12 | /// Contains and . 13 | /// 14 | public partial class ArrayPoolMemoryAllocator 15 | { 16 | /// 17 | /// The buffer implementation of . 18 | /// 19 | private class Buffer : ManagedBufferBase 20 | where T : struct 21 | { 22 | /// 23 | /// The length of the buffer. 24 | /// 25 | private readonly int length; 26 | 27 | /// 28 | /// A weak reference to the source pool. 29 | /// 30 | /// 31 | /// By using a weak reference here, we are making sure that array pools and their retained arrays are always GC-ed 32 | /// after a call to , regardless of having buffer instances still being in use. 33 | /// 34 | private WeakReference> sourcePoolReference; 35 | 36 | public Buffer(byte[] data, int length, ArrayPool sourcePool) 37 | { 38 | this.Data = data; 39 | this.length = length; 40 | this.sourcePoolReference = new WeakReference>(sourcePool); 41 | } 42 | 43 | /// 44 | /// Gets the buffer as a byte array. 45 | /// 46 | protected byte[] Data { get; private set; } 47 | 48 | /// 49 | public override Span GetSpan() => MemoryMarshal.Cast(this.Data.AsSpan()).Slice(0, this.length); 50 | 51 | /// 52 | protected override void Dispose(bool disposing) 53 | { 54 | if (!disposing || this.Data is null || this.sourcePoolReference is null) 55 | { 56 | return; 57 | } 58 | 59 | if (this.sourcePoolReference.TryGetTarget(out ArrayPool pool)) 60 | { 61 | pool.Return(this.Data); 62 | } 63 | 64 | this.sourcePoolReference = null; 65 | this.Data = null; 66 | } 67 | 68 | protected override object GetPinnableObject() => this.Data; 69 | } 70 | 71 | /// 72 | /// The implementation of . 73 | /// 74 | private sealed class ManagedByteBuffer : Buffer, IManagedByteBuffer 75 | { 76 | public ManagedByteBuffer(byte[] data, int length, ArrayPool sourcePool) 77 | : base(data, length, sourcePool) 78 | { 79 | } 80 | 81 | /// 82 | public byte[] Array => this.Data; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/ArrayPoolMemoryAllocator.CommonFactoryMethods.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | namespace SixLabors.Memory 5 | { 6 | /// 7 | /// Contains common factory methods and configuration constants. 8 | /// 9 | public partial class ArrayPoolMemoryAllocator 10 | { 11 | /// 12 | /// The default value for: maximum size of pooled arrays in bytes. 13 | /// Currently set to 24MB, which is equivalent to 8 megapixels of raw RGBA32 data. 14 | /// 15 | internal const int DefaultMaxPooledBufferSizeInBytes = 24 * 1024 * 1024; 16 | 17 | /// 18 | /// The value for: The threshold to pool arrays in which has less buckets for memory safety. 19 | /// 20 | private const int DefaultBufferSelectorThresholdInBytes = 8 * 1024 * 1024; 21 | 22 | /// 23 | /// The default bucket count for . 24 | /// 25 | private const int DefaultLargePoolBucketCount = 6; 26 | 27 | /// 28 | /// The default bucket count for . 29 | /// 30 | private const int DefaultNormalPoolBucketCount = 16; 31 | 32 | /// 33 | /// This is the default. Should be good for most use cases. 34 | /// 35 | /// The memory manager. 36 | public static ArrayPoolMemoryAllocator CreateDefault() 37 | { 38 | return new ArrayPoolMemoryAllocator( 39 | DefaultMaxPooledBufferSizeInBytes, 40 | DefaultBufferSelectorThresholdInBytes, 41 | DefaultLargePoolBucketCount, 42 | DefaultNormalPoolBucketCount); 43 | } 44 | 45 | /// 46 | /// For environments with very limited memory capabilities, only small buffers like image rows are pooled. 47 | /// 48 | /// The memory manager. 49 | public static ArrayPoolMemoryAllocator CreateWithMinimalPooling() 50 | { 51 | return new ArrayPoolMemoryAllocator(64 * 1024, 32 * 1024, 8, 24); 52 | } 53 | 54 | /// 55 | /// For environments with limited memory capabilities, only small array requests are pooled, which can result in reduced throughput. 56 | /// 57 | /// The memory manager. 58 | public static ArrayPoolMemoryAllocator CreateWithModeratePooling() 59 | { 60 | return new ArrayPoolMemoryAllocator(1024 * 1024, 32 * 1024, 16, 24); 61 | } 62 | 63 | /// 64 | /// For environments where memory capabilities are not an issue, the maximum amount of array requests are pooled which results in optimal throughput. 65 | /// 66 | /// The memory manager. 67 | public static ArrayPoolMemoryAllocator CreateWithAggressivePooling() 68 | { 69 | return new ArrayPoolMemoryAllocator(128 * 1024 * 1024, 32 * 1024 * 1024, 16, 32); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/ArrayPoolMemoryAllocator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Buffers; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace SixLabors.Memory 9 | { 10 | /// 11 | /// Implements by allocating memory from . 12 | /// 13 | public sealed partial class ArrayPoolMemoryAllocator : MemoryAllocator 14 | { 15 | private readonly int maxArraysPerBucketNormalPool; 16 | 17 | private readonly int maxArraysPerBucketLargePool; 18 | 19 | /// 20 | /// The for small-to-medium buffers which is not kept clean. 21 | /// 22 | private ArrayPool normalArrayPool; 23 | 24 | /// 25 | /// The for huge buffers, which is not kept clean. 26 | /// 27 | private ArrayPool largeArrayPool; 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | public ArrayPoolMemoryAllocator() 33 | : this(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes) 34 | { 35 | } 36 | 37 | /// 38 | /// Initializes a new instance of the class. 39 | /// 40 | /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. 41 | public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes) 42 | : this(maxPoolSizeInBytes, GetLargeBufferThresholdInBytes(maxPoolSizeInBytes)) 43 | { 44 | } 45 | 46 | /// 47 | /// Initializes a new instance of the class. 48 | /// 49 | /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. 50 | /// Arrays over this threshold will be pooled in which has less buckets for memory safety. 51 | public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes) 52 | : this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, DefaultLargePoolBucketCount, DefaultNormalPoolBucketCount) 53 | { 54 | } 55 | 56 | /// 57 | /// Initializes a new instance of the class. 58 | /// 59 | /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. 60 | /// The threshold to pool arrays in which has less buckets for memory safety. 61 | /// Max arrays per bucket for the large array pool. 62 | /// Max arrays per bucket for the normal array pool. 63 | public ArrayPoolMemoryAllocator(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes, int maxArraysPerBucketLargePool, int maxArraysPerBucketNormalPool) 64 | { 65 | Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); 66 | Guard.MustBeLessThanOrEqualTo(poolSelectorThresholdInBytes, maxPoolSizeInBytes, nameof(poolSelectorThresholdInBytes)); 67 | 68 | this.MaxPoolSizeInBytes = maxPoolSizeInBytes; 69 | this.PoolSelectorThresholdInBytes = poolSelectorThresholdInBytes; 70 | this.maxArraysPerBucketLargePool = maxArraysPerBucketLargePool; 71 | this.maxArraysPerBucketNormalPool = maxArraysPerBucketNormalPool; 72 | 73 | this.InitArrayPools(); 74 | } 75 | 76 | /// 77 | /// Gets the maximum size of pooled arrays in bytes. 78 | /// 79 | public int MaxPoolSizeInBytes { get; } 80 | 81 | /// 82 | /// Gets the threshold to pool arrays in which has less buckets for memory safety. 83 | /// 84 | public int PoolSelectorThresholdInBytes { get; } 85 | 86 | /// 87 | public override void ReleaseRetainedResources() 88 | { 89 | this.InitArrayPools(); 90 | } 91 | 92 | /// 93 | public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) 94 | { 95 | Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); 96 | int itemSizeBytes = Unsafe.SizeOf(); 97 | int bufferSizeInBytes = length * itemSizeBytes; 98 | if (bufferSizeInBytes < 0) 99 | { 100 | throw new ArgumentOutOfRangeException( 101 | nameof(length), 102 | $"{nameof(ArrayPoolMemoryAllocator)} can not allocate {length} elements of {typeof(T).Name}."); 103 | } 104 | 105 | ArrayPool pool = this.GetArrayPool(bufferSizeInBytes); 106 | byte[] byteArray = pool.Rent(bufferSizeInBytes); 107 | 108 | var buffer = new Buffer(byteArray, length, pool); 109 | if (options == AllocationOptions.Clean) 110 | { 111 | buffer.GetSpan().Clear(); 112 | } 113 | 114 | return buffer; 115 | } 116 | 117 | /// 118 | public override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None) 119 | { 120 | Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); 121 | 122 | ArrayPool pool = this.GetArrayPool(length); 123 | byte[] byteArray = pool.Rent(length); 124 | 125 | var buffer = new ManagedByteBuffer(byteArray, length, pool); 126 | if (options == AllocationOptions.Clean) 127 | { 128 | buffer.GetSpan().Clear(); 129 | } 130 | 131 | return buffer; 132 | } 133 | 134 | private static int GetLargeBufferThresholdInBytes(int maxPoolSizeInBytes) 135 | { 136 | return maxPoolSizeInBytes / 4; 137 | } 138 | 139 | private ArrayPool GetArrayPool(int bufferSizeInBytes) 140 | { 141 | return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; 142 | } 143 | 144 | private void InitArrayPools() 145 | { 146 | this.largeArrayPool = ArrayPool.Create(this.MaxPoolSizeInBytes, this.maxArraysPerBucketLargePool); 147 | this.normalArrayPool = ArrayPool.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/IManagedByteBuffer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System.Buffers; 5 | 6 | namespace SixLabors.Memory 7 | { 8 | /// 9 | /// Represents a byte buffer backed by a managed array. Useful for interop with classic .NET API-s. 10 | /// 11 | public interface IManagedByteBuffer : IMemoryOwner 12 | { 13 | /// 14 | /// Gets the managed array backing this buffer instance. 15 | /// 16 | byte[] Array { get; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/Internals/BasicArrayBuffer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace SixLabors.Memory.Internals 8 | { 9 | /// 10 | /// Wraps an array as an instance. 11 | /// 12 | /// 13 | internal class BasicArrayBuffer : ManagedBufferBase 14 | where T : struct 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// The array. 20 | /// The length of the buffer. 21 | public BasicArrayBuffer(T[] array, int length) 22 | { 23 | DebugGuard.MustBeLessThanOrEqualTo(length, array.Length, nameof(length)); 24 | this.Array = array; 25 | this.Length = length; 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | /// The array. 32 | public BasicArrayBuffer(T[] array) 33 | : this(array, array.Length) 34 | { 35 | } 36 | 37 | /// 38 | /// Gets the array. 39 | /// 40 | public T[] Array { get; } 41 | 42 | /// 43 | /// Gets the length. 44 | /// 45 | public int Length { get; } 46 | 47 | /// 48 | public override Span GetSpan() => this.Array.AsSpan(0, this.Length); 49 | 50 | /// 51 | protected override void Dispose(bool disposing) 52 | { 53 | } 54 | 55 | /// 56 | protected override object GetPinnableObject() 57 | { 58 | return this.Array; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/Internals/BasicByteBuffer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | namespace SixLabors.Memory.Internals 5 | { 6 | /// 7 | /// Provides an based on . 8 | /// 9 | internal sealed class BasicByteBuffer : BasicArrayBuffer, IManagedByteBuffer 10 | { 11 | /// 12 | /// Initializes a new instance of the class. 13 | /// 14 | /// The byte array. 15 | internal BasicByteBuffer(byte[] array) 16 | : base(array) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/Internals/ManagedBufferBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System.Buffers; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace SixLabors.Memory.Internals 8 | { 9 | /// 10 | /// Provides a base class for implementations by implementing pinning logic for adaption. 11 | /// 12 | /// The element type. 13 | internal abstract class ManagedBufferBase : MemoryManager 14 | where T : struct 15 | { 16 | private GCHandle pinHandle; 17 | 18 | /// 19 | public override unsafe MemoryHandle Pin(int elementIndex = 0) 20 | { 21 | if (!this.pinHandle.IsAllocated) 22 | { 23 | this.pinHandle = GCHandle.Alloc(this.GetPinnableObject(), GCHandleType.Pinned); 24 | } 25 | 26 | void* ptr = (void*)this.pinHandle.AddrOfPinnedObject(); 27 | return new MemoryHandle(ptr, this.pinHandle); 28 | } 29 | 30 | /// 31 | public override void Unpin() 32 | { 33 | if (this.pinHandle.IsAllocated) 34 | { 35 | this.pinHandle.Free(); 36 | } 37 | } 38 | 39 | /// 40 | /// Gets the object that should be pinned. 41 | /// 42 | /// The pinnable . 43 | protected abstract object GetPinnableObject(); 44 | } 45 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/MemoryAllocator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System.Buffers; 5 | 6 | namespace SixLabors.Memory 7 | { 8 | /// 9 | /// Memory managers are used to allocate memory for image processing operations. 10 | /// 11 | public abstract class MemoryAllocator 12 | { 13 | /// 14 | /// Allocates an , holding a of length . 15 | /// 16 | /// Type of the data stored in the buffer. 17 | /// Size of the buffer to allocate. 18 | /// The allocation options. 19 | /// A buffer of values of type . 20 | public abstract IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) 21 | where T : struct; 22 | 23 | /// 24 | /// Allocates an . 25 | /// 26 | /// The requested buffer length. 27 | /// The allocation options. 28 | /// The . 29 | public abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None); 30 | 31 | /// 32 | /// Releases all retained resources not being in use. 33 | /// Eg: by resetting array pools and letting GC to free the arrays. 34 | /// 35 | public virtual void ReleaseRetainedResources() 36 | { 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/SixLabors.Core/Memory/SimpleGcMemoryAllocator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Buffers; 6 | using SixLabors.Memory.Internals; 7 | 8 | namespace SixLabors.Memory 9 | { 10 | /// 11 | /// Implements by newing up arrays by the GC on every allocation requests. 12 | /// 13 | public sealed class SimpleGcMemoryAllocator : MemoryAllocator 14 | { 15 | /// 16 | public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) 17 | { 18 | Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); 19 | 20 | return new BasicArrayBuffer(new T[length]); 21 | } 22 | 23 | /// 24 | public override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None) 25 | { 26 | Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length)); 27 | 28 | return new BasicByteBuffer(new byte[length]); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Primitives/Matrix3x2Extensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System.Numerics; 5 | 6 | namespace SixLabors.Primitives 7 | { 8 | /// 9 | /// Extension methods for the struct. 10 | /// 11 | public static class Matrix3x2Extensions 12 | { 13 | /// 14 | /// Creates a translation matrix from the given vector. 15 | /// 16 | /// The translation position. 17 | /// A translation matrix. 18 | public static Matrix3x2 CreateTranslation(PointF position) => Matrix3x2.CreateTranslation(position); 19 | 20 | /// 21 | /// Creates a scale matrix that is offset by a given center point. 22 | /// 23 | /// Value to scale by on the X-axis. 24 | /// Value to scale by on the Y-axis. 25 | /// The center point. 26 | /// A scaling matrix. 27 | public static Matrix3x2 CreateScale(float xScale, float yScale, PointF centerPoint) => Matrix3x2.CreateScale(xScale, yScale, centerPoint); 28 | 29 | /// 30 | /// Creates a scale matrix from the given vector scale. 31 | /// 32 | /// The scale to use. 33 | /// A scaling matrix. 34 | public static Matrix3x2 CreateScale(SizeF scales) => Matrix3x2.CreateScale(scales); 35 | 36 | /// 37 | /// Creates a scale matrix from the given vector scale with an offset from the given center point. 38 | /// 39 | /// The scale to use. 40 | /// The center offset. 41 | /// A scaling matrix. 42 | public static Matrix3x2 CreateScale(SizeF scales, PointF centerPoint) => Matrix3x2.CreateScale(scales, centerPoint); 43 | 44 | /// 45 | /// Creates a scale matrix that scales uniformly with the given scale with an offset from the given center. 46 | /// 47 | /// The uniform scale to use. 48 | /// The center offset. 49 | /// A scaling matrix. 50 | public static Matrix3x2 CreateScale(float scale, PointF centerPoint) => Matrix3x2.CreateScale(scale, centerPoint); 51 | 52 | /// 53 | /// Creates a skew matrix from the given angles in degrees. 54 | /// 55 | /// The X angle, in degrees. 56 | /// The Y angle, in degrees. 57 | /// A skew matrix. 58 | public static Matrix3x2 CreateSkewDegrees(float degreesX, float degreesY) => Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); 59 | 60 | /// 61 | /// Creates a skew matrix from the given angles in radians and a center point. 62 | /// 63 | /// The X angle, in radians. 64 | /// The Y angle, in radians. 65 | /// The center point. 66 | /// A skew matrix. 67 | public static Matrix3x2 CreateSkew(float radiansX, float radiansY, PointF centerPoint) => Matrix3x2.CreateSkew(radiansX, radiansY, centerPoint); 68 | 69 | /// 70 | /// Creates a skew matrix from the given angles in degrees and a center point. 71 | /// 72 | /// The X angle, in degrees. 73 | /// The Y angle, in degrees. 74 | /// The center point. 75 | /// A skew matrix. 76 | public static Matrix3x2 CreateSkewDegrees(float degreesX, float degreesY, PointF centerPoint) => Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), centerPoint); 77 | 78 | /// 79 | /// Creates a rotation matrix using the given rotation in degrees. 80 | /// 81 | /// The amount of rotation, in degrees. 82 | /// A rotation matrix. 83 | public static Matrix3x2 CreateRotationDegrees(float degrees) => Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees)); 84 | 85 | /// 86 | /// Creates a rotation matrix using the given rotation in radians and a center point. 87 | /// 88 | /// The amount of rotation, in radians. 89 | /// The center point. 90 | /// A rotation matrix. 91 | public static Matrix3x2 CreateRotation(float radians, PointF centerPoint) => Matrix3x2.CreateRotation(radians, centerPoint); 92 | 93 | /// 94 | /// Creates a rotation matrix using the given rotation in degrees and a center point. 95 | /// 96 | /// The amount of rotation, in degrees. 97 | /// The center point. 98 | /// A rotation matrix. 99 | public static Matrix3x2 CreateRotationDegrees(float degrees, PointF centerPoint) => Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees), centerPoint); 100 | } 101 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Primitives/Point.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.ComponentModel; 6 | using System.Numerics; 7 | using System.Runtime.CompilerServices; 8 | 9 | namespace SixLabors.Primitives 10 | { 11 | /// 12 | /// Represents an ordered pair of integer x- and y-coordinates that defines a point in 13 | /// a two-dimensional plane. 14 | /// 15 | /// 16 | /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, 17 | /// as it avoids the need to create new values for modification operations. 18 | /// 19 | public struct Point : IEquatable 20 | { 21 | /// 22 | /// Represents a that has X and Y values set to zero. 23 | /// 24 | public static readonly Point Empty = default; 25 | 26 | /// 27 | /// Initializes a new instance of the struct. 28 | /// 29 | /// The horizontal and vertical position of the point. 30 | public Point(int value) 31 | : this() 32 | { 33 | this.X = LowInt16(value); 34 | this.Y = HighInt16(value); 35 | } 36 | 37 | /// 38 | /// Initializes a new instance of the struct. 39 | /// 40 | /// The horizontal position of the point. 41 | /// The vertical position of the point. 42 | public Point(int x, int y) 43 | : this() 44 | { 45 | this.X = x; 46 | this.Y = y; 47 | } 48 | 49 | /// 50 | /// Initializes a new instance of the struct from the given . 51 | /// 52 | /// The size. 53 | public Point(Size size) 54 | { 55 | this.X = size.Width; 56 | this.Y = size.Height; 57 | } 58 | 59 | /// 60 | /// Gets or sets the x-coordinate of this . 61 | /// 62 | public int X { get; set; } 63 | 64 | /// 65 | /// Gets or sets the y-coordinate of this . 66 | /// 67 | public int Y { get; set; } 68 | 69 | /// 70 | /// Gets a value indicating whether this is empty. 71 | /// 72 | [EditorBrowsable(EditorBrowsableState.Never)] 73 | public bool IsEmpty => this.Equals(Empty); 74 | 75 | /// 76 | /// Creates a with the coordinates of the specified . 77 | /// 78 | /// The point. 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static implicit operator PointF(Point point) => new PointF(point.X, point.Y); 81 | 82 | /// 83 | /// Creates a with the coordinates of the specified . 84 | /// 85 | /// The point. 86 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 87 | public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y); 88 | 89 | /// 90 | /// Creates a with the coordinates of the specified . 91 | /// 92 | /// The point. 93 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 94 | public static explicit operator Size(Point point) => new Size(point.X, point.Y); 95 | 96 | /// 97 | /// Negates the given point by multiplying all values by -1. 98 | /// 99 | /// The source point. 100 | /// The negated point. 101 | public static Point operator -(Point value) => new Point(-value.X, -value.Y); 102 | 103 | /// 104 | /// Translates a by a given . 105 | /// 106 | /// The point on the left hand of the operand. 107 | /// The size on the right hand of the operand. 108 | /// 109 | /// The . 110 | /// 111 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 112 | public static Point operator +(Point point, Size size) => Add(point, size); 113 | 114 | /// 115 | /// Translates a by the negative of a given . 116 | /// 117 | /// The point on the left hand of the operand. 118 | /// The size on the right hand of the operand. 119 | /// The . 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public static Point operator -(Point point, Size size) => Subtract(point, size); 122 | 123 | /// 124 | /// Multiplies by a producing . 125 | /// 126 | /// Multiplier of type . 127 | /// Multiplicand of type . 128 | /// Product of type . 129 | public static Point operator *(int left, Point right) => Multiply(right, left); 130 | 131 | /// 132 | /// Multiplies by a producing . 133 | /// 134 | /// Multiplicand of type . 135 | /// Multiplier of type . 136 | /// Product of type . 137 | public static Point operator *(Point left, int right) => Multiply(left, right); 138 | 139 | /// 140 | /// Divides by a producing . 141 | /// 142 | /// Dividend of type . 143 | /// Divisor of type . 144 | /// Result of type . 145 | public static Point operator /(Point left, int right) 146 | => new Point(left.X / right, left.Y / right); 147 | 148 | /// 149 | /// Compares two objects for equality. 150 | /// 151 | /// The on the left side of the operand. 152 | /// The on the right side of the operand. 153 | /// 154 | /// True if the current left is equal to the parameter; otherwise, false. 155 | /// 156 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 157 | public static bool operator ==(Point left, Point right) => left.Equals(right); 158 | 159 | /// 160 | /// Compares two objects for inequality. 161 | /// 162 | /// The on the left side of the operand. 163 | /// The on the right side of the operand. 164 | /// 165 | /// True if the current left is unequal to the parameter; otherwise, false. 166 | /// 167 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 168 | public static bool operator !=(Point left, Point right) => !left.Equals(right); 169 | 170 | /// 171 | /// Translates a by the negative of a given . 172 | /// 173 | /// The point on the left hand of the operand. 174 | /// The size on the right hand of the operand. 175 | /// The . 176 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 177 | public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height)); 178 | 179 | /// 180 | /// Translates a by the negative of a given value. 181 | /// 182 | /// The point on the left hand of the operand. 183 | /// The value on the right hand of the operand. 184 | /// The . 185 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 186 | public static Point Multiply(Point point, int value) => new Point(unchecked(point.X * value), unchecked(point.Y * value)); 187 | 188 | /// 189 | /// Translates a by the negative of a given . 190 | /// 191 | /// The point on the left hand of the operand. 192 | /// The size on the right hand of the operand. 193 | /// The . 194 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 195 | public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height)); 196 | 197 | /// 198 | /// Converts a to a by performing a ceiling operation on all the coordinates. 199 | /// 200 | /// The point. 201 | /// The . 202 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 203 | public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y))); 204 | 205 | /// 206 | /// Converts a to a by performing a round operation on all the coordinates. 207 | /// 208 | /// The point. 209 | /// The . 210 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 211 | public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); 212 | 213 | /// 214 | /// Converts a to a by performing a round operation on all the coordinates. 215 | /// 216 | /// The vector. 217 | /// The . 218 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 219 | public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y))); 220 | 221 | /// 222 | /// Converts a to a by performing a truncate operation on all the coordinates. 223 | /// 224 | /// The point. 225 | /// The . 226 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 227 | public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y)); 228 | 229 | /// 230 | /// Transforms a point by a specified 3x2 matrix. 231 | /// 232 | /// The point to transform. 233 | /// The transformation matrix used. 234 | /// The transformed . 235 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 236 | public static Point Transform(Point point, Matrix3x2 matrix) => Round(Vector2.Transform(new Vector2(point.X, point.Y), matrix)); 237 | 238 | /// 239 | /// Deconstructs this point into two integers. 240 | /// 241 | /// The out value for X. 242 | /// The out value for Y. 243 | public void Deconstruct(out int x, out int y) 244 | { 245 | x = this.X; 246 | y = this.Y; 247 | } 248 | 249 | /// 250 | /// Translates this by the specified amount. 251 | /// 252 | /// The amount to offset the x-coordinate. 253 | /// The amount to offset the y-coordinate. 254 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 255 | public void Offset(int dx, int dy) 256 | { 257 | unchecked 258 | { 259 | this.X += dx; 260 | this.Y += dy; 261 | } 262 | } 263 | 264 | /// 265 | /// Translates this by the specified amount. 266 | /// 267 | /// The used offset this . 268 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 269 | public void Offset(Point point) => this.Offset(point.X, point.Y); 270 | 271 | /// 272 | public override int GetHashCode() => HashCode.Combine(this.X, this.Y); 273 | 274 | /// 275 | public override string ToString() => $"Point [ X={this.X}, Y={this.Y} ]"; 276 | 277 | /// 278 | public override bool Equals(object obj) => obj is Point other && this.Equals(other); 279 | 280 | /// 281 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 282 | public bool Equals(Point other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); 283 | 284 | private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); 285 | 286 | private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); 287 | } 288 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Primitives/PointF.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.ComponentModel; 6 | using System.Numerics; 7 | using System.Runtime.CompilerServices; 8 | 9 | namespace SixLabors.Primitives 10 | { 11 | /// 12 | /// Represents an ordered pair of single precision floating point x- and y-coordinates that defines a point in 13 | /// a two-dimensional plane. 14 | /// 15 | /// 16 | /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, 17 | /// as it avoids the need to create new values for modification operations. 18 | /// 19 | public struct PointF : IEquatable 20 | { 21 | /// 22 | /// Represents a that has X and Y values set to zero. 23 | /// 24 | public static readonly PointF Empty = default; 25 | 26 | /// 27 | /// Initializes a new instance of the struct. 28 | /// 29 | /// The horizontal position of the point. 30 | /// The vertical position of the point. 31 | public PointF(float x, float y) 32 | : this() 33 | { 34 | this.X = x; 35 | this.Y = y; 36 | } 37 | 38 | /// 39 | /// Initializes a new instance of the struct from the given . 40 | /// 41 | /// The size. 42 | public PointF(SizeF size) 43 | { 44 | this.X = size.Width; 45 | this.Y = size.Height; 46 | } 47 | 48 | /// 49 | /// Gets or sets the x-coordinate of this . 50 | /// 51 | public float X { get; set; } 52 | 53 | /// 54 | /// Gets or sets the y-coordinate of this . 55 | /// 56 | public float Y { get; set; } 57 | 58 | /// 59 | /// Gets a value indicating whether this is empty. 60 | /// 61 | [EditorBrowsable(EditorBrowsableState.Never)] 62 | public bool IsEmpty => this.Equals(Empty); 63 | 64 | /// 65 | /// Creates a with the coordinates of the specified . 66 | /// 67 | /// The vector. 68 | /// 69 | /// The . 70 | /// 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public static implicit operator PointF(Vector2 vector) => new PointF(vector.X, vector.Y); 73 | 74 | /// 75 | /// Creates a with the coordinates of the specified . 76 | /// 77 | /// The point. 78 | /// 79 | /// The . 80 | /// 81 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 82 | public static implicit operator Vector2(PointF point) => new Vector2(point.X, point.Y); 83 | 84 | /// 85 | /// Creates a with the coordinates of the specified by truncating each of the coordinates. 86 | /// 87 | /// The point. 88 | /// 89 | /// The . 90 | /// 91 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 92 | public static explicit operator Point(PointF point) => Point.Truncate(point); 93 | 94 | /// 95 | /// Negates the given point by multiplying all values by -1. 96 | /// 97 | /// The source point. 98 | /// The negated point. 99 | public static PointF operator -(PointF value) => new PointF(-value.X, -value.Y); 100 | 101 | /// 102 | /// Translates a by a given . 103 | /// 104 | /// The point on the left hand of the operand. 105 | /// The size on the right hand of the operand. 106 | /// 107 | /// The . 108 | /// 109 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 110 | public static PointF operator +(PointF point, SizeF size) => Add(point, size); 111 | 112 | /// 113 | /// Translates a by the negative of a given . 114 | /// 115 | /// The point on the left hand of the operand. 116 | /// The size on the right hand of the operand. 117 | /// The . 118 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 119 | public static PointF operator -(PointF point, PointF size) => Subtract(point, size); 120 | 121 | /// 122 | /// Translates a by a given . 123 | /// 124 | /// The point on the left hand of the operand. 125 | /// The size on the right hand of the operand. 126 | /// 127 | /// The . 128 | /// 129 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 | public static PointF operator +(PointF point, PointF size) => Add(point, size); 131 | 132 | /// 133 | /// Translates a by the negative of a given . 134 | /// 135 | /// The point on the left hand of the operand. 136 | /// The size on the right hand of the operand. 137 | /// The . 138 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 | public static PointF operator -(PointF point, SizeF size) => Subtract(point, size); 140 | 141 | /// 142 | /// Multiplies by a producing . 143 | /// 144 | /// Multiplier of type . 145 | /// Multiplicand of type . 146 | /// Product of type . 147 | public static PointF operator *(float left, PointF right) => Multiply(right, left); 148 | 149 | /// 150 | /// Multiplies by a producing . 151 | /// 152 | /// Multiplicand of type . 153 | /// Multiplier of type . 154 | /// Product of type . 155 | public static PointF operator *(PointF left, float right) => Multiply(left, right); 156 | 157 | /// 158 | /// Divides by a producing . 159 | /// 160 | /// Dividend of type . 161 | /// Divisor of type . 162 | /// Result of type . 163 | public static PointF operator /(PointF left, float right) 164 | => new PointF(left.X / right, left.Y / right); 165 | 166 | /// 167 | /// Compares two objects for equality. 168 | /// 169 | /// 170 | /// The on the left side of the operand. 171 | /// 172 | /// 173 | /// The on the right side of the operand. 174 | /// 175 | /// 176 | /// True if the current left is equal to the parameter; otherwise, false. 177 | /// 178 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 179 | public static bool operator ==(PointF left, PointF right) => left.Equals(right); 180 | 181 | /// 182 | /// Compares two objects for inequality. 183 | /// 184 | /// 185 | /// The on the left side of the operand. 186 | /// 187 | /// 188 | /// The on the right side of the operand. 189 | /// 190 | /// 191 | /// True if the current left is unequal to the parameter; otherwise, false. 192 | /// 193 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 194 | public static bool operator !=(PointF left, PointF right) => !left.Equals(right); 195 | 196 | /// 197 | /// Translates a by the given . 198 | /// 199 | /// The point on the left hand of the operand. 200 | /// The size on the right hand of the operand. 201 | /// The . 202 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 203 | public static PointF Add(PointF point, SizeF size) => new PointF(point.X + size.Width, point.Y + size.Height); 204 | 205 | /// 206 | /// Translates a by the given . 207 | /// 208 | /// The point on the left hand of the operand. 209 | /// The point on the right hand of the operand. 210 | /// The . 211 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 212 | public static PointF Add(PointF point, PointF pointb) => new PointF(point.X + pointb.X, point.Y + pointb.Y); 213 | 214 | /// 215 | /// Translates a by the negative of a given . 216 | /// 217 | /// The point on the left hand of the operand. 218 | /// The size on the right hand of the operand. 219 | /// The . 220 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 221 | public static PointF Subtract(PointF point, SizeF size) => new PointF(point.X - size.Width, point.Y - size.Height); 222 | 223 | /// 224 | /// Translates a by the negative of a given . 225 | /// 226 | /// The point on the left hand of the operand. 227 | /// The point on the right hand of the operand. 228 | /// The . 229 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 230 | public static PointF Subtract(PointF point, PointF pointb) => new PointF(point.X - pointb.X, point.Y - pointb.Y); 231 | 232 | /// 233 | /// Translates a by the multiplying the X and Y by the given value. 234 | /// 235 | /// The point on the left hand of the operand. 236 | /// The value on the right hand of the operand. 237 | /// The . 238 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 239 | public static PointF Multiply(PointF point, float right) => new PointF(point.X * right, point.Y * right); 240 | 241 | /// 242 | /// Transforms a point by a specified 3x2 matrix. 243 | /// 244 | /// The point to transform. 245 | /// The transformation matrix used. 246 | /// The transformed . 247 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 248 | public static PointF Transform(PointF point, Matrix3x2 matrix) => Vector2.Transform(point, matrix); 249 | 250 | /// 251 | /// Deconstructs this point into two floats. 252 | /// 253 | /// The out value for X. 254 | /// The out value for Y. 255 | public void Deconstruct(out float x, out float y) 256 | { 257 | x = this.X; 258 | y = this.Y; 259 | } 260 | 261 | /// 262 | /// Translates this by the specified amount. 263 | /// 264 | /// The amount to offset the x-coordinate. 265 | /// The amount to offset the y-coordinate. 266 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 267 | public void Offset(float dx, float dy) 268 | { 269 | this.X += dx; 270 | this.Y += dy; 271 | } 272 | 273 | /// 274 | /// Translates this by the specified amount. 275 | /// 276 | /// The used offset this . 277 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 278 | public void Offset(PointF point) => this.Offset(point.X, point.Y); 279 | 280 | /// 281 | public override int GetHashCode() => HashCode.Combine(this.X, this.Y); 282 | 283 | /// 284 | public override string ToString() => $"PointF [ X={this.X}, Y={this.Y} ]"; 285 | 286 | /// 287 | public override bool Equals(object obj) => obj is PointF && this.Equals((PointF)obj); 288 | 289 | /// 290 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 291 | public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); 292 | } 293 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Primitives/SizeF.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.ComponentModel; 6 | using System.Numerics; 7 | using System.Runtime.CompilerServices; 8 | 9 | namespace SixLabors.Primitives 10 | { 11 | /// 12 | /// Stores an ordered pair of single precision floating points, which specify a height and width. 13 | /// 14 | /// 15 | /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, 16 | /// as it avoids the need to create new values for modification operations. 17 | /// 18 | public struct SizeF : IEquatable 19 | { 20 | /// 21 | /// Represents a that has Width and Height values set to zero. 22 | /// 23 | public static readonly SizeF Empty = default; 24 | 25 | /// 26 | /// Initializes a new instance of the struct. 27 | /// 28 | /// The width of the size. 29 | /// The height of the size. 30 | public SizeF(float width, float height) 31 | { 32 | this.Width = width; 33 | this.Height = height; 34 | } 35 | 36 | /// 37 | /// Initializes a new instance of the struct. 38 | /// 39 | /// The size. 40 | public SizeF(SizeF size) 41 | : this() 42 | { 43 | this.Width = size.Width; 44 | this.Height = size.Height; 45 | } 46 | 47 | /// 48 | /// Initializes a new instance of the struct from the given . 49 | /// 50 | /// The point. 51 | public SizeF(PointF point) 52 | { 53 | this.Width = point.X; 54 | this.Height = point.Y; 55 | } 56 | 57 | /// 58 | /// Gets or sets the width of this . 59 | /// 60 | public float Width { get; set; } 61 | 62 | /// 63 | /// Gets or sets the height of this . 64 | /// 65 | public float Height { get; set; } 66 | 67 | /// 68 | /// Gets a value indicating whether this is empty. 69 | /// 70 | [EditorBrowsable(EditorBrowsableState.Never)] 71 | public bool IsEmpty => this.Equals(Empty); 72 | 73 | /// 74 | /// Creates a with the coordinates of the specified . 75 | /// 76 | /// The point. 77 | /// 78 | /// The . 79 | /// 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public static implicit operator Vector2(SizeF point) => new Vector2(point.Width, point.Height); 82 | 83 | /// 84 | /// Creates a with the dimensions of the specified by truncating each of the dimensions. 85 | /// 86 | /// The size. 87 | /// 88 | /// The . 89 | /// 90 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 91 | public static explicit operator Size(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); 92 | 93 | /// 94 | /// Converts the given into a . 95 | /// 96 | /// The size. 97 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 98 | public static explicit operator PointF(SizeF size) => new PointF(size.Width, size.Height); 99 | 100 | /// 101 | /// Computes the sum of adding two sizes. 102 | /// 103 | /// The size on the left hand of the operand. 104 | /// The size on the right hand of the operand. 105 | /// 106 | /// The . 107 | /// 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static SizeF operator +(SizeF left, SizeF right) => Add(left, right); 110 | 111 | /// 112 | /// Computes the difference left by subtracting one size from another. 113 | /// 114 | /// The size on the left hand of the operand. 115 | /// The size on the right hand of the operand. 116 | /// 117 | /// The . 118 | /// 119 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 120 | public static SizeF operator -(SizeF left, SizeF right) => Subtract(left, right); 121 | 122 | /// 123 | /// Multiplies by a producing . 124 | /// 125 | /// Multiplier of type . 126 | /// Multiplicand of type . 127 | /// Product of type . 128 | public static SizeF operator *(float left, SizeF right) => Multiply(right, left); 129 | 130 | /// 131 | /// Multiplies by a producing . 132 | /// 133 | /// Multiplicand of type . 134 | /// Multiplier of type . 135 | /// Product of type . 136 | public static SizeF operator *(SizeF left, float right) => Multiply(left, right); 137 | 138 | /// 139 | /// Divides by a producing . 140 | /// 141 | /// Dividend of type . 142 | /// Divisor of type . 143 | /// Result of type . 144 | public static SizeF operator /(SizeF left, float right) 145 | => new SizeF(left.Width / right, left.Height / right); 146 | 147 | /// 148 | /// Compares two objects for equality. 149 | /// 150 | /// The size on the left hand of the operand. 151 | /// The size on the right hand of the operand. 152 | /// 153 | /// True if the current left is equal to the parameter; otherwise, false. 154 | /// 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public static bool operator ==(SizeF left, SizeF right) => left.Equals(right); 157 | 158 | /// 159 | /// Compares two objects for inequality. 160 | /// 161 | /// The size on the left hand of the operand. 162 | /// The size on the right hand of the operand. 163 | /// 164 | /// True if the current left is unequal to the parameter; otherwise, false. 165 | /// 166 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 167 | public static bool operator !=(SizeF left, SizeF right) => !left.Equals(right); 168 | 169 | /// 170 | /// Performs vector addition of two objects. 171 | /// 172 | /// The size on the left hand of the operand. 173 | /// The size on the right hand of the operand. 174 | /// The . 175 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 176 | public static SizeF Add(SizeF left, SizeF right) => new SizeF(left.Width + right.Width, left.Height + right.Height); 177 | 178 | /// 179 | /// Contracts a by another . 180 | /// 181 | /// The size on the left hand of the operand. 182 | /// The size on the right hand of the operand. 183 | /// The . 184 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 185 | public static SizeF Subtract(SizeF left, SizeF right) => new SizeF(left.Width - right.Width, left.Height - right.Height); 186 | 187 | /// 188 | /// Transforms a size by the given matrix. 189 | /// 190 | /// The source size. 191 | /// The transformation matrix. 192 | /// A transformed size. 193 | public static SizeF Transform(SizeF size, Matrix3x2 matrix) 194 | { 195 | var v = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); 196 | 197 | return new SizeF(v.X, v.Y); 198 | } 199 | 200 | /// 201 | /// Deconstructs this size into two floats. 202 | /// 203 | /// The out value for the width. 204 | /// The out value for the height. 205 | public void Deconstruct(out float width, out float height) 206 | { 207 | width = this.Width; 208 | height = this.Height; 209 | } 210 | 211 | /// 212 | public override int GetHashCode() => HashCode.Combine(this.Width, this.Height); 213 | 214 | /// 215 | public override string ToString() => $"SizeF [ Width={this.Width}, Height={this.Height} ]"; 216 | 217 | /// 218 | public override bool Equals(object obj) => obj is SizeF && this.Equals((SizeF)obj); 219 | 220 | /// 221 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 222 | public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); 223 | 224 | /// 225 | /// Multiplies by a producing . 226 | /// 227 | /// Multiplicand of type . 228 | /// Multiplier of type . 229 | /// Product of type SizeF. 230 | private static SizeF Multiply(SizeF size, float multiplier) => 231 | new SizeF(size.Width * multiplier, size.Height * multiplier); 232 | } 233 | } -------------------------------------------------------------------------------- /src/SixLabors.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | // Ensure the internals can be tested. 7 | [assembly: InternalsVisibleTo("SixLabors.Core.Tests")] -------------------------------------------------------------------------------- /src/SixLabors.Core/SixLabors.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Low level primitives for use across Six Labors projects. 5 | $(packageversion) 6 | 0.1.0-alpha2 7 | Six Labors 8 | netstandard1.3;netstandard2.0;netcoreapp2.0;netcoreapp2.1; 9 | true 10 | true 11 | SixLabors.Core 12 | SixLabors.Core 13 | rectangle;point;size,primitives 14 | https://raw.githubusercontent.com/SixLabors/Branding/master/icons/core/sixlabors.core.128.png 15 | https://github.com/SixLabors/Core 16 | http://www.apache.org/licenses/LICENSE-2.0 17 | git 18 | https://github.com/SixLabors/Core 19 | Copyright (c) Six Labors and contributors. 20 | full 21 | SixLabors 22 | 7.3 23 | 24 | 25 | 26 | ..\..\shared-infrastructure\SixLabors.ruleset 27 | 28 | 29 | 30 | 31 | $(DefineConstants);SUPPORTS_MATHF 32 | 33 | 34 | 35 | $(DefineConstants);SUPPORTS_HASHCODE 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | All 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/SixLabors.Core/SixLabors.Core.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /tests/CodeCoverage/.gitignore: -------------------------------------------------------------------------------- 1 | /OpenCover* -------------------------------------------------------------------------------- /tests/CodeCoverage/CodeCoverage.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cd tests\CodeCoverage 4 | 5 | nuget restore packages.config -PackagesDirectory . 6 | 7 | cd .. 8 | cd .. 9 | 10 | dotnet restore SixLabors.Core.sln 11 | dotnet build SixLabors.Core.sln --no-incremental -c release /p:codecov=true 12 | 13 | tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\SixLabors.Core.Tests\SixLabors.Core.Tests.csproj --no-build -c release" -searchdirs:"tests\SixLabors.Core.Tests\bin\Release\netcoreapp2.1" -register:user -output:.\SixLabors.Core.Coverage.xml -hideskipped:All -returntargetcode -oldStyle -filter:"+[SixLabors.*]*" 14 | 15 | if %errorlevel% neq 0 exit /b %errorlevel% 16 | 17 | SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH% 18 | pip install codecov 19 | codecov -f "SixLabors.Core.Coverage.xml" -------------------------------------------------------------------------------- /tests/CodeCoverage/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Helpers/DebugGuardTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | // tell this file to enable debug conditional method calls, i.e. all the debug guard calls 5 | #define DEBUG 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Diagnostics; 10 | using System.Linq; 11 | using System.Reflection; 12 | using Xunit; 13 | 14 | namespace SixLabors.Helpers.Tests 15 | { 16 | public class DebugGuardTests 17 | { 18 | private class Foo 19 | { 20 | } 21 | 22 | [Fact] 23 | public void AllStaticMethodsOnOnDebugGuardHaveDEBUGConditional() 24 | { 25 | IEnumerable methods = typeof(DebugGuard).GetTypeInfo().GetMethods() 26 | .Where(x => x.IsStatic); 27 | 28 | foreach (MethodInfo m in methods) 29 | { 30 | IEnumerable attribs = m.GetCustomAttributes(); 31 | Assert.True(attribs.Select(x => x.ConditionString).Contains("DEBUG"), $"Method '{m.Name}' does not have [Conditional(\"DEBUG\")] set."); 32 | } 33 | } 34 | 35 | [Fact] 36 | public void NotNull_WhenNull_Throws() 37 | { 38 | Foo foo = null; 39 | Assert.Throws(() => Guard.NotNull(foo, nameof(foo))); 40 | } 41 | 42 | [Fact] 43 | public void NotNull_WhenNotNull() 44 | { 45 | var foo = new Foo(); 46 | Guard.NotNull(foo, nameof(foo)); 47 | } 48 | 49 | [Theory] 50 | [InlineData(null, true)] 51 | [InlineData("", true)] 52 | [InlineData(" ", true)] 53 | [InlineData("$", false)] 54 | [InlineData("lol", false)] 55 | public void NotNullOrWhiteSpace(string str, bool shouldThrow) 56 | { 57 | if (shouldThrow) 58 | { 59 | Assert.ThrowsAny(() => Guard.NotNullOrWhiteSpace(str, nameof(str))); 60 | } 61 | else 62 | { 63 | Guard.NotNullOrWhiteSpace(str, nameof(str)); 64 | } 65 | } 66 | 67 | [Theory] 68 | [InlineData(true)] 69 | [InlineData(false)] 70 | public void IsTrue(bool value) 71 | { 72 | if (!value) 73 | { 74 | Assert.Throws(() => Guard.IsTrue(value, nameof(value), "Boo!")); 75 | } 76 | else 77 | { 78 | Guard.IsTrue(value, nameof(value), "Boo."); 79 | } 80 | } 81 | 82 | [Theory] 83 | [InlineData(true)] 84 | [InlineData(false)] 85 | public void IsFalse(bool value) 86 | { 87 | if (value) 88 | { 89 | Assert.Throws(() => Guard.IsFalse(value, nameof(value), "Boo!")); 90 | } 91 | else 92 | { 93 | Guard.IsFalse(value, nameof(value), "Boo."); 94 | } 95 | } 96 | 97 | public static readonly TheoryData SizeCheckData = new TheoryData 98 | { 99 | { 0, 0, false }, 100 | { 1, 1, false }, 101 | { 1, 0, false }, 102 | { 13, 13, false }, 103 | { 20, 13, false }, 104 | { 12, 13, true }, 105 | { 0, 1, true }, 106 | }; 107 | 108 | [Theory] 109 | [MemberData(nameof(SizeCheckData))] 110 | public void MustBeSizedAtLeast(int length, int minLength, bool shouldThrow) 111 | { 112 | int[] data = new int[length]; 113 | 114 | if (shouldThrow) 115 | { 116 | Assert.Throws(() => Guard.MustBeSizedAtLeast((Span)data, minLength, nameof(data))); 117 | Assert.Throws(() => Guard.MustBeSizedAtLeast((ReadOnlySpan)data, minLength, nameof(data))); 118 | } 119 | else 120 | { 121 | Guard.MustBeSizedAtLeast((Span)data, minLength, nameof(data)); 122 | Guard.MustBeSizedAtLeast((ReadOnlySpan)data, minLength, nameof(data)); 123 | } 124 | } 125 | 126 | [Theory] 127 | [MemberData(nameof(SizeCheckData))] 128 | public void DestinationShouldNotBeTooShort(int destLength, int sourceLength, bool shouldThrow) 129 | { 130 | int[] dest = new int[destLength]; 131 | int[] source = new int[sourceLength]; 132 | 133 | if (shouldThrow) 134 | { 135 | Assert.Throws(() => Guard.DestinationShouldNotBeTooShort((Span)source, (Span)dest, nameof(dest))); 136 | Assert.Throws(() => Guard.DestinationShouldNotBeTooShort((ReadOnlySpan)source, (Span)dest, nameof(dest))); 137 | } 138 | else 139 | { 140 | Guard.DestinationShouldNotBeTooShort((Span)source, (Span)dest, nameof(dest)); 141 | Guard.DestinationShouldNotBeTooShort((ReadOnlySpan)source, (Span)dest, nameof(dest)); 142 | } 143 | } 144 | 145 | [Fact] 146 | public void MustBeLessThan_IsLess_ThrowsNoException() 147 | { 148 | DebugGuard.MustBeLessThan(0, 1, "myParamName"); 149 | } 150 | 151 | [Theory] 152 | [InlineData(2, 1)] 153 | [InlineData(1, 1)] 154 | public void MustBeLessThan_IsGreaterOrEqual_ThrowsNoException(int value, int max) 155 | { 156 | ArgumentOutOfRangeException exception = Assert.Throws( 157 | () => DebugGuard.MustBeLessThan(value, max, "myParamName")); 158 | 159 | Assert.Equal("myParamName", exception.ParamName); 160 | Assert.Contains($"Value {value} must be less than {max}.", exception.Message); 161 | } 162 | 163 | [Theory] 164 | [InlineData(0, 1)] 165 | [InlineData(1, 1)] 166 | public void MustBeLessThanOrEqualTo_IsLessOrEqual_ThrowsNoException(int value, int max) 167 | { 168 | DebugGuard.MustBeLessThanOrEqualTo(value, max, "myParamName"); 169 | } 170 | 171 | [Fact] 172 | public void MustBeLessThanOrEqualTo_IsGreater_ThrowsNoException() 173 | { 174 | ArgumentOutOfRangeException exception = Assert.Throws(() => DebugGuard.MustBeLessThanOrEqualTo(2, 1, "myParamName")); 175 | 176 | Assert.Equal("myParamName", exception.ParamName); 177 | Assert.Contains($"Value 2 must be less than or equal to 1.", exception.Message); 178 | } 179 | 180 | [Fact] 181 | public void MustBeGreaterThan_IsGreater_ThrowsNoException() 182 | { 183 | DebugGuard.MustBeGreaterThan(2, 1, "myParamName"); 184 | } 185 | 186 | [Theory] 187 | [InlineData(1, 2)] 188 | [InlineData(1, 1)] 189 | public void MustBeGreaterThan_IsLessOrEqual_ThrowsNoException(int value, int min) 190 | { 191 | ArgumentOutOfRangeException exception = Assert.Throws( 192 | () => DebugGuard.MustBeGreaterThan(value, min, "myParamName")); 193 | 194 | Assert.Equal("myParamName", exception.ParamName); 195 | Assert.Contains($"Value {value} must be greater than {min}.", exception.Message); 196 | } 197 | 198 | [Theory] 199 | [InlineData(2, 1)] 200 | [InlineData(1, 1)] 201 | public void MustBeGreaterThanOrEqualTo_IsGreaterOrEqual_ThrowsNoException(int value, int min) 202 | { 203 | DebugGuard.MustBeGreaterThanOrEqualTo(value, min, "myParamName"); 204 | } 205 | 206 | [Fact] 207 | public void MustBeGreaterThanOrEqualTo_IsLess_ThrowsNoException() 208 | { 209 | ArgumentOutOfRangeException exception = Assert.Throws( 210 | () => DebugGuard.MustBeGreaterThanOrEqualTo(1, 2, "myParamName")); 211 | 212 | Assert.Equal("myParamName", exception.ParamName); 213 | Assert.Contains($"Value 1 must be greater than or equal to 2.", exception.Message); 214 | } 215 | 216 | [Theory] 217 | [InlineData(new int[] { 1, 2 }, 1)] 218 | [InlineData(new int[] { 1, 2 }, 2)] 219 | public void MustBeSizedAtLeast_Array_LengthIsGreaterOrEqual_ThrowsNoException(int[] value, int minLength) 220 | { 221 | DebugGuard.MustBeSizedAtLeast(value, minLength, "myParamName"); 222 | } 223 | 224 | [Fact] 225 | public void MustBeSizedAtLeast_Array_LengthIsLess_ThrowsException() 226 | { 227 | ArgumentException exception = Assert.Throws( 228 | () => DebugGuard.MustBeSizedAtLeast(new int[] { 1, 2 }, 3, "myParamName")); 229 | 230 | Assert.Equal("myParamName", exception.ParamName); 231 | Assert.Contains($"The size must be at least 3.", exception.Message); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Helpers/FloatRoundingComparer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Numerics; 7 | 8 | namespace SixLabors.Tests.Helpers 9 | { 10 | /// 11 | /// Allows the comparison of single-precision floating point values by precision. 12 | /// 13 | public struct FloatRoundingComparer : IEqualityComparer, IEqualityComparer 14 | { 15 | /// 16 | /// Initializes a new instance of the struct. 17 | /// 18 | /// The number of decimal places (valid values: 0-7). 19 | public FloatRoundingComparer(int precision) 20 | { 21 | Guard.MustBeBetweenOrEqualTo(precision, 0, 7, nameof(precision)); 22 | this.Precision = precision; 23 | } 24 | 25 | /// 26 | /// Gets the number of decimal places (valid values: 0-7). 27 | /// 28 | public int Precision { get; } 29 | 30 | /// 31 | public bool Equals(float x, float y) 32 | { 33 | float xp = (float)Math.Round(x, this.Precision, MidpointRounding.AwayFromZero); 34 | float yp = (float)Math.Round(y, this.Precision, MidpointRounding.AwayFromZero); 35 | 36 | return Comparer.Default.Compare(xp, yp) == 0; 37 | } 38 | 39 | /// 40 | public bool Equals(Vector4 x, Vector4 y) 41 | { 42 | return this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z) && this.Equals(x.W, y.W); 43 | } 44 | 45 | /// 46 | public int GetHashCode(float obj) 47 | { 48 | unchecked 49 | { 50 | int hashCode = obj.GetHashCode(); 51 | hashCode = (hashCode * 397) ^ this.Precision.GetHashCode(); 52 | return hashCode; 53 | } 54 | } 55 | 56 | /// 57 | public int GetHashCode(Vector4 obj) => HashCode.Combine(obj, this.Precision); 58 | } 59 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Helpers/GeometryUtilitiesTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using Xunit; 6 | 7 | namespace SixLabors.Tests.Helpers 8 | { 9 | public class GeometryUtilitiesTests 10 | { 11 | [Fact] 12 | public void Convert_Degree_To_Radian() 13 | => Assert.Equal((float)(Math.PI / 2D), GeometryUtilities.DegreeToRadian(90F), new FloatRoundingComparer(6)); 14 | 15 | [Fact] 16 | public void Convert_Radian_To_Degree() 17 | => Assert.Equal(60F, GeometryUtilities.RadianToDegree((float)(Math.PI / 3D)), new FloatRoundingComparer(5)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Helpers/GuardTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using Xunit; 6 | 7 | namespace SixLabors.Helpers.Tests 8 | { 9 | public class GuardTests 10 | { 11 | private class Foo 12 | { 13 | } 14 | 15 | [Fact] 16 | public void NotNull_WhenNull_Throws() 17 | { 18 | Foo foo = null; 19 | Assert.Throws(() => Guard.NotNull(foo, nameof(foo))); 20 | } 21 | 22 | [Fact] 23 | public void NotNull_WhenNotNull() 24 | { 25 | Foo foo = new Foo(); 26 | Guard.NotNull(foo, nameof(foo)); 27 | } 28 | 29 | [Theory] 30 | [InlineData(null, true)] 31 | [InlineData("", true)] 32 | [InlineData(" ", true)] 33 | [InlineData("$", false)] 34 | [InlineData("lol", false)] 35 | public void NotNullOrWhiteSpace(string str, bool shouldThrow) 36 | { 37 | if (shouldThrow) 38 | { 39 | Assert.ThrowsAny(() => Guard.NotNullOrWhiteSpace(str, nameof(str))); 40 | } 41 | else 42 | { 43 | Guard.NotNullOrWhiteSpace(str, nameof(str)); 44 | } 45 | } 46 | 47 | [Theory] 48 | [InlineData(true)] 49 | [InlineData(false)] 50 | public void IsTrue(bool value) 51 | { 52 | if (!value) 53 | { 54 | Assert.Throws(() => Guard.IsTrue(value, nameof(value), "Boo!")); 55 | } 56 | else 57 | { 58 | Guard.IsTrue(value, nameof(value), "Boo."); 59 | } 60 | } 61 | 62 | [Theory] 63 | [InlineData(true)] 64 | [InlineData(false)] 65 | public void IsFalse(bool value) 66 | { 67 | if (value) 68 | { 69 | Assert.Throws(() => Guard.IsFalse(value, nameof(value), "Boo!")); 70 | } 71 | else 72 | { 73 | Guard.IsFalse(value, nameof(value), "Boo."); 74 | } 75 | } 76 | 77 | public static readonly TheoryData SizeCheckData = new TheoryData 78 | { 79 | { 0, 0, false }, 80 | { 1, 1, false }, 81 | { 1, 0, false }, 82 | { 13, 13, false }, 83 | { 20, 13, false }, 84 | { 12, 13, true }, 85 | { 0, 1, true }, 86 | }; 87 | 88 | [Theory] 89 | [MemberData(nameof(SizeCheckData))] 90 | public void MustBeSizedAtLeast(int length, int minLength, bool shouldThrow) 91 | { 92 | int[] data = new int[length]; 93 | 94 | if (shouldThrow) 95 | { 96 | Assert.Throws(() => Guard.MustBeSizedAtLeast((Span)data, minLength, nameof(data))); 97 | Assert.Throws(() => Guard.MustBeSizedAtLeast((ReadOnlySpan)data, minLength, nameof(data))); 98 | } 99 | else 100 | { 101 | Guard.MustBeSizedAtLeast((Span)data, minLength, nameof(data)); 102 | Guard.MustBeSizedAtLeast((ReadOnlySpan)data, minLength, nameof(data)); 103 | } 104 | } 105 | 106 | [Theory] 107 | [MemberData(nameof(SizeCheckData))] 108 | public void DestinationShouldNotBeTooShort(int destLength, int sourceLength, bool shouldThrow) 109 | { 110 | int[] dest = new int[destLength]; 111 | int[] source = new int[sourceLength]; 112 | 113 | if (shouldThrow) 114 | { 115 | Assert.Throws(() => Guard.DestinationShouldNotBeTooShort((Span)source, (Span)dest, nameof(dest))); 116 | Assert.Throws(() => Guard.DestinationShouldNotBeTooShort((ReadOnlySpan)source, (Span)dest, nameof(dest))); 117 | } 118 | else 119 | { 120 | Guard.DestinationShouldNotBeTooShort((Span)source, (Span)dest, nameof(dest)); 121 | Guard.DestinationShouldNotBeTooShort((ReadOnlySpan)source, (Span)dest, nameof(dest)); 122 | } 123 | } 124 | 125 | [Fact] 126 | public void MustBeLessThan_IsLess_ThrowsNoException() 127 | { 128 | Guard.MustBeLessThan(0, 1, "myParamName"); 129 | } 130 | 131 | [Theory] 132 | [InlineData(2, 1)] 133 | [InlineData(1, 1)] 134 | public void MustBeLessThan_IsGreaterOrEqual_ThrowsNoException(int value, int max) 135 | { 136 | ArgumentOutOfRangeException exception = Assert.Throws(() => 137 | { 138 | Guard.MustBeLessThan(value, max, "myParamName"); 139 | }); 140 | 141 | Assert.Equal("myParamName", exception.ParamName); 142 | Assert.Contains($"Value {value} must be less than {max}.", exception.Message); 143 | } 144 | 145 | [Theory] 146 | [InlineData(0, 1)] 147 | [InlineData(1, 1)] 148 | public void MustBeLessThanOrEqualTo_IsLessOrEqual_ThrowsNoException(int value, int max) 149 | { 150 | Guard.MustBeLessThanOrEqualTo(value, max, "myParamName"); 151 | } 152 | 153 | [Fact] 154 | public void MustBeLessThanOrEqualTo_IsGreater_ThrowsNoException() 155 | { 156 | ArgumentOutOfRangeException exception = Assert.Throws(() => 157 | { 158 | Guard.MustBeLessThanOrEqualTo(2, 1, "myParamName"); 159 | }); 160 | 161 | Assert.Equal("myParamName", exception.ParamName); 162 | Assert.Contains($"Value 2 must be less than or equal to 1.", exception.Message); 163 | } 164 | 165 | [Fact] 166 | public void MustBeGreaterThan_IsGreater_ThrowsNoException() 167 | { 168 | Guard.MustBeGreaterThan(2, 1, "myParamName"); 169 | } 170 | 171 | [Theory] 172 | [InlineData(1, 2)] 173 | [InlineData(1, 1)] 174 | public void MustBeGreaterThan_IsLessOrEqual_ThrowsNoException(int value, int min) 175 | { 176 | ArgumentOutOfRangeException exception = Assert.Throws(() => 177 | { 178 | Guard.MustBeGreaterThan(value, min, "myParamName"); 179 | }); 180 | 181 | Assert.Equal("myParamName", exception.ParamName); 182 | Assert.Contains($"Value {value} must be greater than {min}.", exception.Message); 183 | } 184 | 185 | [Theory] 186 | [InlineData(2, 1)] 187 | [InlineData(1, 1)] 188 | public void MustBeGreaterThanOrEqualTo_IsGreaterOrEqual_ThrowsNoException(int value, int min) 189 | { 190 | Guard.MustBeGreaterThanOrEqualTo(value, min, "myParamName"); 191 | } 192 | 193 | [Fact] 194 | public void MustBeGreaterThanOrEqualTo_IsLess_ThrowsNoException() 195 | { 196 | ArgumentOutOfRangeException exception = Assert.Throws(() => 197 | { 198 | Guard.MustBeGreaterThanOrEqualTo(1, 2, "myParamName"); 199 | }); 200 | 201 | Assert.Equal("myParamName", exception.ParamName); 202 | Assert.Contains($"Value 1 must be greater than or equal to 2.", exception.Message); 203 | } 204 | 205 | [Theory] 206 | [InlineData(1, 1, 3)] 207 | [InlineData(2, 1, 3)] 208 | [InlineData(3, 1, 3)] 209 | public void MustBeBetweenOrEqualTo_IsBetweenOrEqual_ThrowsNoException(int value, int min, int max) 210 | { 211 | Guard.MustBeBetweenOrEqualTo(value, min, max, "myParamName"); 212 | } 213 | 214 | [Theory] 215 | [InlineData(0, 1, 3)] 216 | [InlineData(4, 1, 3)] 217 | public void MustBeBetweenOrEqualTo_IsLessOrGreater_ThrowsNoException(int value, int min, int max) 218 | { 219 | ArgumentOutOfRangeException exception = Assert.Throws(() => 220 | { 221 | Guard.MustBeBetweenOrEqualTo(value, min, max, "myParamName"); 222 | }); 223 | 224 | Assert.Equal("myParamName", exception.ParamName); 225 | Assert.Contains($"Value {value} must be greater than or equal to {min} and less than or equal to {max}.", exception.Message); 226 | } 227 | 228 | [Theory] 229 | [InlineData(2, 1)] 230 | [InlineData(2, 2)] 231 | public void MustBeSizedAtLeast_Array_LengthIsGreaterOrEqual_ThrowsNoException(int valueLength, int minLength) 232 | { 233 | Guard.MustBeSizedAtLeast(new int[valueLength], minLength, "myParamName"); 234 | } 235 | 236 | [Fact] 237 | public void MustBeSizedAtLeast_Array_LengthIsLess_ThrowsException() 238 | { 239 | ArgumentException exception = Assert.Throws(() => 240 | { 241 | Guard.MustBeSizedAtLeast(new int[] { 1, 2 }, 3, "myParamName"); 242 | }); 243 | 244 | Assert.Equal("myParamName", exception.ParamName); 245 | Assert.Contains("The size must be at least 3", exception.Message); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Helpers/MathFTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using Xunit; 6 | 7 | namespace SixLabors.Tests.Helpers 8 | { 9 | public class MathFTests 10 | { 11 | [Fact] 12 | public void MathF_PI_Is_Equal() 13 | { 14 | Assert.Equal(MathF.PI, (float)Math.PI); 15 | } 16 | 17 | [Fact] 18 | public void MathF_Ceililng_Is_Equal() 19 | { 20 | Assert.Equal(MathF.Ceiling(0.3333F), (float)Math.Ceiling(0.3333F)); 21 | } 22 | 23 | [Fact] 24 | public void MathF_Cos_Is_Equal() 25 | { 26 | Assert.Equal(MathF.Cos(0.3333F), (float)Math.Cos(0.3333F)); 27 | } 28 | 29 | [Fact] 30 | public void MathF_Abs_Is_Equal() 31 | { 32 | Assert.Equal(MathF.Abs(-0.3333F), (float)Math.Abs(-0.3333F)); 33 | } 34 | 35 | [Fact] 36 | public void MathF_Atan2_Is_Equal() 37 | { 38 | Assert.Equal(MathF.Atan2(1.2345F, 1.2345F), (float)Math.Atan2(1.2345F, 1.2345F)); 39 | } 40 | 41 | [Fact] 42 | public void MathF_Exp_Is_Equal() 43 | { 44 | Assert.Equal(MathF.Exp(1.2345F), (float)Math.Exp(1.2345F)); 45 | } 46 | 47 | [Fact] 48 | public void MathF_Floor_Is_Equal() 49 | { 50 | Assert.Equal(MathF.Floor(1.2345F), (float)Math.Floor(1.2345F)); 51 | } 52 | 53 | [Fact] 54 | public void MathF_Min_Is_Equal() 55 | { 56 | Assert.Equal(MathF.Min(1.2345F, 5.4321F), (float)Math.Min(1.2345F, 5.4321F)); 57 | } 58 | 59 | [Fact] 60 | public void MathF_Max_Is_Equal() 61 | { 62 | Assert.Equal(MathF.Max(1.2345F, 5.4321F), (float)Math.Max(1.2345F, 5.4321F)); 63 | } 64 | 65 | [Fact] 66 | public void MathF_Pow_Is_Equal() 67 | { 68 | Assert.Equal(MathF.Pow(1.2345F, 5.4321F), (float)Math.Pow(1.2345F, 5.4321F)); 69 | } 70 | 71 | [Fact] 72 | public void MathF_Round_Is_Equal() 73 | { 74 | Assert.Equal(MathF.Round(1.2345F), (float)Math.Round(1.2345F)); 75 | } 76 | 77 | [Fact] 78 | public void MathF_Round_With_Midpoint_Is_Equal() 79 | { 80 | Assert.Equal(MathF.Round(1.2345F, MidpointRounding.AwayFromZero), (float)Math.Round(1.2345F, MidpointRounding.AwayFromZero)); 81 | } 82 | 83 | [Fact] 84 | public void MathF_Sin_Is_Equal() 85 | { 86 | Assert.Equal(MathF.Sin(1.2345F), (float)Math.Sin(1.2345F)); 87 | } 88 | 89 | [Fact] 90 | public void MathF_Sqrt_Is_Equal() 91 | { 92 | Assert.Equal(MathF.Sqrt(2F), (float)Math.Sqrt(2F)); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Memory/ArrayPoolMemoryAllocatorTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | // ReSharper disable InconsistentNaming 5 | using System; 6 | using System.Buffers; 7 | using System.Runtime.CompilerServices; 8 | using System.Runtime.InteropServices; 9 | using SixLabors.Tests; 10 | using Xunit; 11 | 12 | namespace SixLabors.Memory.Tests 13 | { 14 | public class ArrayPoolMemoryAllocatorTests 15 | { 16 | private const int MaxPooledBufferSizeInBytes = 2048; 17 | 18 | private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2; 19 | 20 | private MemoryAllocator MemoryAllocator { get; set; } = 21 | new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); 22 | 23 | /// 24 | /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location. 25 | /// 26 | private bool CheckIsRentingPooledBuffer(int length) 27 | where T : struct 28 | { 29 | IMemoryOwner buffer = this.MemoryAllocator.Allocate(length); 30 | ref T ptrToPrevPosition0 = ref buffer.GetReference(); 31 | buffer.Dispose(); 32 | 33 | buffer = this.MemoryAllocator.Allocate(length); 34 | bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.GetReference()); 35 | buffer.Dispose(); 36 | 37 | return sameBuffers; 38 | } 39 | 40 | public class BufferTests : BufferTestSuite 41 | { 42 | public BufferTests() 43 | : base(new ArrayPoolMemoryAllocator(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes)) 44 | { 45 | } 46 | } 47 | 48 | public class Constructor 49 | { 50 | [Fact] 51 | public void WhenBothParametersPassedByUser() 52 | { 53 | var mgr = new ArrayPoolMemoryAllocator(1111, 666); 54 | Assert.Equal(1111, mgr.MaxPoolSizeInBytes); 55 | Assert.Equal(666, mgr.PoolSelectorThresholdInBytes); 56 | } 57 | 58 | [Fact] 59 | public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdValueIsAutoCalculated() 60 | { 61 | var mgr = new ArrayPoolMemoryAllocator(5000); 62 | Assert.Equal(5000, mgr.MaxPoolSizeInBytes); 63 | Assert.True(mgr.PoolSelectorThresholdInBytes < mgr.MaxPoolSizeInBytes); 64 | } 65 | 66 | [Fact] 67 | public void When_PoolSelectorThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_ExceptionIsThrown() 68 | { 69 | Assert.ThrowsAny(() => new ArrayPoolMemoryAllocator(100, 200)); 70 | } 71 | } 72 | 73 | [Theory] 74 | [InlineData(32)] 75 | [InlineData(512)] 76 | [InlineData(MaxPooledBufferSizeInBytes - 1)] 77 | public void SmallBuffersArePooled_OfByte(int size) 78 | { 79 | Assert.True(this.CheckIsRentingPooledBuffer(size)); 80 | } 81 | 82 | [Theory] 83 | [InlineData(128 * 1024 * 1024)] 84 | [InlineData(MaxPooledBufferSizeInBytes + 1)] 85 | public void LargeBuffersAreNotPooled_OfByte(int size) 86 | { 87 | if (!TestEnvironment.Is64BitProcess) 88 | { 89 | // can lead to OutOfMemoryException 90 | return; 91 | } 92 | 93 | Assert.False(this.CheckIsRentingPooledBuffer(size)); 94 | } 95 | 96 | [Fact] 97 | public unsafe void SmallBuffersArePooled_OfBigValueType() 98 | { 99 | int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) - 1; 100 | 101 | Assert.True(this.CheckIsRentingPooledBuffer(count)); 102 | } 103 | 104 | [Fact] 105 | public unsafe void LaregeBuffersAreNotPooled_OfBigValueType() 106 | { 107 | if (!TestEnvironment.Is64BitProcess) 108 | { 109 | // can lead to OutOfMemoryException 110 | return; 111 | } 112 | 113 | int count = (MaxPooledBufferSizeInBytes / sizeof(LargeStruct)) + 1; 114 | 115 | Assert.False(this.CheckIsRentingPooledBuffer(count)); 116 | } 117 | 118 | [Theory] 119 | [InlineData(AllocationOptions.None)] 120 | [InlineData(AllocationOptions.Clean)] 121 | public void CleaningRequests_AreControlledByAllocationParameter_Clean(AllocationOptions options) 122 | { 123 | using (IMemoryOwner firstAlloc = this.MemoryAllocator.Allocate(42)) 124 | { 125 | firstAlloc.GetSpan().Fill(666); 126 | } 127 | 128 | using (IMemoryOwner secondAlloc = this.MemoryAllocator.Allocate(42, options)) 129 | { 130 | int expected = options == AllocationOptions.Clean ? 0 : 666; 131 | Assert.Equal(expected, secondAlloc.GetSpan()[0]); 132 | } 133 | } 134 | 135 | [Theory] 136 | [InlineData(false)] 137 | [InlineData(true)] 138 | public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) 139 | { 140 | IMemoryOwner buffer = this.MemoryAllocator.Allocate(32); 141 | ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.GetSpan()); 142 | 143 | if (!keepBufferAlive) 144 | { 145 | buffer.Dispose(); 146 | } 147 | 148 | this.MemoryAllocator.ReleaseRetainedResources(); 149 | 150 | buffer = this.MemoryAllocator.Allocate(32); 151 | 152 | Assert.False(Unsafe.AreSame(ref ptrToPrev0, ref buffer.GetReference())); 153 | } 154 | 155 | [Fact] 156 | public void ReleaseRetainedResources_DisposingPreviouslyAllocatedBuffer_IsAllowed() 157 | { 158 | IMemoryOwner buffer = this.MemoryAllocator.Allocate(32); 159 | this.MemoryAllocator.ReleaseRetainedResources(); 160 | buffer.Dispose(); 161 | } 162 | 163 | [Fact] 164 | public void AllocationOverLargeArrayThreshold_UsesDifferentPool() 165 | { 166 | if (!TestEnvironment.Is64BitProcess) 167 | { 168 | // can lead to OutOfMemoryException 169 | return; 170 | } 171 | 172 | const int ArrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int); 173 | 174 | IMemoryOwner small = this.MemoryAllocator.Allocate(ArrayLengthThreshold - 1); 175 | ref int ptr2Small = ref small.GetReference(); 176 | small.Dispose(); 177 | 178 | IMemoryOwner large = this.MemoryAllocator.Allocate(ArrayLengthThreshold + 1); 179 | 180 | Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference())); 181 | } 182 | 183 | [Fact] 184 | public void CreateWithAggressivePooling() 185 | { 186 | if (!TestEnvironment.Is64BitProcess) 187 | { 188 | // can lead to OutOfMemoryException 189 | return; 190 | } 191 | 192 | this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithAggressivePooling(); 193 | 194 | Assert.True(this.CheckIsRentingPooledBuffer(4096 * 4096)); 195 | } 196 | 197 | [Fact] 198 | public void CreateDefault() 199 | { 200 | if (!TestEnvironment.Is64BitProcess) 201 | { 202 | // can lead to OutOfMemoryException 203 | return; 204 | } 205 | 206 | this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateDefault(); 207 | 208 | Assert.False(this.CheckIsRentingPooledBuffer(2 * 4096 * 4096)); 209 | Assert.True(this.CheckIsRentingPooledBuffer(2048 * 2048)); 210 | } 211 | 212 | [Fact] 213 | public void CreateWithModeratePooling() 214 | { 215 | if (!TestEnvironment.Is64BitProcess) 216 | { 217 | // can lead to OutOfMemoryException 218 | return; 219 | } 220 | 221 | this.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); 222 | 223 | Assert.False(this.CheckIsRentingPooledBuffer(2048 * 2048)); 224 | Assert.True(this.CheckIsRentingPooledBuffer(1024 * 16)); 225 | } 226 | 227 | [StructLayout(LayoutKind.Sequential)] 228 | private struct Rgba32 229 | { 230 | private readonly uint dummy; 231 | } 232 | 233 | private const int SizeOfLargeStruct = MaxPooledBufferSizeInBytes / 5; 234 | 235 | [StructLayout(LayoutKind.Explicit, Size = SizeOfLargeStruct)] 236 | private struct LargeStruct 237 | { 238 | } 239 | 240 | [Theory] 241 | [InlineData(-1)] 242 | [InlineData((int.MaxValue / SizeOfLargeStruct) + 1)] 243 | public void AllocateIncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) 244 | { 245 | ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.Allocate(length)); 246 | Assert.Equal("length", ex.ParamName); 247 | } 248 | 249 | [Theory] 250 | [InlineData(-1)] 251 | public void AllocateManagedByteBuffer_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) 252 | { 253 | ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.AllocateManagedByteBuffer(length)); 254 | Assert.Equal("length", ex.ParamName); 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Memory/BufferExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Buffers; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | 9 | namespace SixLabors.Memory.Tests 10 | { 11 | internal static class BufferExtensions 12 | { 13 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 14 | public static Span GetSpan(this IMemoryOwner buffer) 15 | => buffer.Memory.Span; 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static int Length(this IMemoryOwner buffer) 19 | => buffer.GetSpan().Length; 20 | 21 | public static ref T GetReference(this IMemoryOwner buffer) 22 | where T : struct => 23 | ref MemoryMarshal.GetReference(buffer.GetSpan()); 24 | } 25 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Memory/BufferTestSuite.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Buffers; 6 | using System.Runtime.CompilerServices; 7 | using System.Runtime.InteropServices; 8 | using Xunit; 9 | 10 | // ReSharper disable InconsistentNaming 11 | namespace SixLabors.Memory.Tests 12 | { 13 | /// 14 | /// Inherit this class to test an implementation (provided by ). 15 | /// 16 | public abstract class BufferTestSuite 17 | { 18 | protected BufferTestSuite(MemoryAllocator memoryAllocator) 19 | { 20 | this.MemoryAllocator = memoryAllocator; 21 | } 22 | 23 | protected MemoryAllocator MemoryAllocator { get; } 24 | 25 | public struct CustomStruct : IEquatable 26 | { 27 | public long A; 28 | 29 | public byte B; 30 | 31 | public float C; 32 | 33 | public CustomStruct(long a, byte b, float c) 34 | { 35 | this.A = a; 36 | this.B = b; 37 | this.C = c; 38 | } 39 | 40 | public bool Equals(CustomStruct other) 41 | { 42 | return this.A == other.A && this.B == other.B && this.C.Equals(other.C); 43 | } 44 | 45 | public override bool Equals(object obj) 46 | { 47 | return obj is CustomStruct other && this.Equals(other); 48 | } 49 | 50 | public override int GetHashCode() 51 | { 52 | unchecked 53 | { 54 | int hashCode = this.A.GetHashCode(); 55 | hashCode = (hashCode * 397) ^ this.B.GetHashCode(); 56 | hashCode = (hashCode * 397) ^ this.C.GetHashCode(); 57 | return hashCode; 58 | } 59 | } 60 | } 61 | 62 | public static readonly TheoryData LenthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; 63 | 64 | [Theory] 65 | [MemberData(nameof(LenthValues))] 66 | public void HasCorrectLength_byte(int desiredLength) 67 | { 68 | this.TestHasCorrectLength(desiredLength); 69 | } 70 | 71 | [Theory] 72 | [MemberData(nameof(LenthValues))] 73 | public void HasCorrectLength_float(int desiredLength) 74 | { 75 | this.TestHasCorrectLength(desiredLength); 76 | } 77 | 78 | [Theory] 79 | [MemberData(nameof(LenthValues))] 80 | public void HasCorrectLength_CustomStruct(int desiredLength) 81 | { 82 | this.TestHasCorrectLength(desiredLength); 83 | } 84 | 85 | private void TestHasCorrectLength(int desiredLength) 86 | where T : struct 87 | { 88 | using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(desiredLength)) 89 | { 90 | Assert.Equal(desiredLength, buffer.GetSpan().Length); 91 | } 92 | } 93 | 94 | [Theory] 95 | [MemberData(nameof(LenthValues))] 96 | public void CanAllocateCleanBuffer_byte(int desiredLength) 97 | { 98 | this.TestCanAllocateCleanBuffer(desiredLength, false); 99 | this.TestCanAllocateCleanBuffer(desiredLength, true); 100 | } 101 | 102 | [Theory] 103 | [MemberData(nameof(LenthValues))] 104 | public void CanAllocateCleanBuffer_double(int desiredLength) 105 | { 106 | this.TestCanAllocateCleanBuffer(desiredLength); 107 | } 108 | 109 | [Theory] 110 | [MemberData(nameof(LenthValues))] 111 | public void CanAllocateCleanBuffer_CustomStruct(int desiredLength) 112 | { 113 | this.TestCanAllocateCleanBuffer(desiredLength); 114 | } 115 | 116 | private IMemoryOwner Allocate(int desiredLength, AllocationOptions options, bool managedByteBuffer) 117 | where T : struct 118 | { 119 | if (managedByteBuffer) 120 | { 121 | if (!(this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength, options) is IMemoryOwner buffer)) 122 | { 123 | throw new InvalidOperationException("typeof(T) != typeof(byte)"); 124 | } 125 | 126 | return buffer; 127 | } 128 | 129 | return this.MemoryAllocator.Allocate(desiredLength, options); 130 | } 131 | 132 | private void TestCanAllocateCleanBuffer(int desiredLength, bool testManagedByteBuffer = false) 133 | where T : struct, IEquatable 134 | { 135 | ReadOnlySpan expected = new T[desiredLength]; 136 | 137 | for (int i = 0; i < 10; i++) 138 | { 139 | using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.Clean, testManagedByteBuffer)) 140 | { 141 | Assert.True(buffer.GetSpan().SequenceEqual(expected)); 142 | } 143 | } 144 | } 145 | 146 | [Theory] 147 | [MemberData(nameof(LenthValues))] 148 | public void SpanPropertyIsAlwaysTheSame_int(int desiredLength) 149 | { 150 | this.TestSpanPropertyIsAlwaysTheSame(desiredLength); 151 | } 152 | 153 | [Theory] 154 | [MemberData(nameof(LenthValues))] 155 | public void SpanPropertyIsAlwaysTheSame_byte(int desiredLength) 156 | { 157 | this.TestSpanPropertyIsAlwaysTheSame(desiredLength, false); 158 | this.TestSpanPropertyIsAlwaysTheSame(desiredLength, true); 159 | } 160 | 161 | private void TestSpanPropertyIsAlwaysTheSame(int desiredLength, bool testManagedByteBuffer = false) 162 | where T : struct 163 | { 164 | using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) 165 | { 166 | ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan()); 167 | ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan()); 168 | ref T c = ref MemoryMarshal.GetReference(buffer.GetSpan()); 169 | 170 | Assert.True(Unsafe.AreSame(ref a, ref b)); 171 | Assert.True(Unsafe.AreSame(ref b, ref c)); 172 | } 173 | } 174 | 175 | [Theory] 176 | [MemberData(nameof(LenthValues))] 177 | public void WriteAndReadElements_float(int desiredLength) 178 | { 179 | this.TestWriteAndReadElements(desiredLength, x => x * 1.2f); 180 | } 181 | 182 | [Theory] 183 | [MemberData(nameof(LenthValues))] 184 | public void WriteAndReadElements_byte(int desiredLength) 185 | { 186 | this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), false); 187 | this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), true); 188 | } 189 | 190 | private void TestWriteAndReadElements(int desiredLength, Func getExpectedValue, bool testManagedByteBuffer = false) 191 | where T : struct 192 | { 193 | using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) 194 | { 195 | T[] expectedVals = new T[buffer.Length()]; 196 | 197 | for (int i = 0; i < buffer.Length(); i++) 198 | { 199 | Span span = buffer.GetSpan(); 200 | expectedVals[i] = getExpectedValue(i); 201 | span[i] = expectedVals[i]; 202 | } 203 | 204 | for (int i = 0; i < buffer.Length(); i++) 205 | { 206 | Span span = buffer.GetSpan(); 207 | Assert.Equal(expectedVals[i], span[i]); 208 | } 209 | } 210 | } 211 | 212 | [Theory] 213 | [MemberData(nameof(LenthValues))] 214 | public void IndexingSpan_WhenOutOfRange_Throws_byte(int desiredLength) 215 | { 216 | this.TestIndexOutOfRangeShouldThrow(desiredLength, false); 217 | this.TestIndexOutOfRangeShouldThrow(desiredLength, true); 218 | } 219 | 220 | [Theory] 221 | [MemberData(nameof(LenthValues))] 222 | public void IndexingSpan_WhenOutOfRange_Throws_long(int desiredLength) 223 | { 224 | this.TestIndexOutOfRangeShouldThrow(desiredLength); 225 | } 226 | 227 | [Theory] 228 | [MemberData(nameof(LenthValues))] 229 | public void IndexingSpan_WhenOutOfRange_Throws_CustomStruct(int desiredLength) 230 | { 231 | this.TestIndexOutOfRangeShouldThrow(desiredLength); 232 | } 233 | 234 | private T TestIndexOutOfRangeShouldThrow(int desiredLength, bool testManagedByteBuffer = false) 235 | where T : struct, IEquatable 236 | { 237 | var dummy = default(T); 238 | 239 | using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) 240 | { 241 | Assert.ThrowsAny( 242 | () => 243 | { 244 | Span span = buffer.GetSpan(); 245 | dummy = span[desiredLength]; 246 | }); 247 | 248 | Assert.ThrowsAny( 249 | () => 250 | { 251 | Span span = buffer.GetSpan(); 252 | dummy = span[desiredLength + 1]; 253 | }); 254 | 255 | Assert.ThrowsAny( 256 | () => 257 | { 258 | Span span = buffer.GetSpan(); 259 | dummy = span[desiredLength + 42]; 260 | }); 261 | } 262 | 263 | return dummy; 264 | } 265 | 266 | [Theory] 267 | [InlineData(1)] 268 | [InlineData(7)] 269 | [InlineData(1024)] 270 | [InlineData(6666)] 271 | public void ManagedByteBuffer_ArrayIsCorrect(int desiredLength) 272 | { 273 | using (IManagedByteBuffer buffer = this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength)) 274 | { 275 | ref byte array0 = ref buffer.Array[0]; 276 | ref byte span0 = ref buffer.GetReference(); 277 | 278 | Assert.True(Unsafe.AreSame(ref span0, ref array0)); 279 | Assert.True(buffer.Array.Length >= buffer.GetSpan().Length); 280 | } 281 | } 282 | 283 | [Fact] 284 | public void GetMemory_ReturnsValidMemory() 285 | { 286 | using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(42)) 287 | { 288 | Span span0 = buffer.GetSpan(); 289 | span0[10].A = 30; 290 | Memory memory = buffer.Memory; 291 | 292 | Assert.Equal(42, memory.Length); 293 | Span span1 = memory.Span; 294 | 295 | Assert.Equal(42, span1.Length); 296 | Assert.Equal(30, span1[10].A); 297 | } 298 | } 299 | 300 | [Fact] 301 | public unsafe void GetMemory_ResultIsPinnable() 302 | { 303 | using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(42)) 304 | { 305 | Span span0 = buffer.GetSpan(); 306 | span0[10] = 30; 307 | 308 | Memory memory = buffer.Memory; 309 | 310 | using (MemoryHandle h = memory.Pin()) 311 | { 312 | int* ptr = (int*)h.Pointer; 313 | Assert.Equal(30, ptr[10]); 314 | } 315 | } 316 | } 317 | } 318 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Memory/SimpleGcMemoryAllocatorTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using Xunit; 7 | 8 | namespace SixLabors.Memory.Tests 9 | { 10 | public class SimpleGcMemoryAllocatorTests 11 | { 12 | public class BufferTests : BufferTestSuite 13 | { 14 | public BufferTests() 15 | : base(new SimpleGcMemoryAllocator()) 16 | { 17 | } 18 | } 19 | 20 | protected SimpleGcMemoryAllocator MemoryAllocator { get; } = new SimpleGcMemoryAllocator(); 21 | 22 | [Theory] 23 | [InlineData(-1)] 24 | public void Allocate_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) 25 | { 26 | ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.Allocate(length)); 27 | Assert.Equal("length", ex.ParamName); 28 | } 29 | 30 | [Theory] 31 | [InlineData(-1)] 32 | public void AllocateManagedByteBuffer_IncorrectAmount_ThrowsCorrect_ArgumentOutOfRangeException(int length) 33 | { 34 | ArgumentOutOfRangeException ex = Assert.Throws(() => this.MemoryAllocator.AllocateManagedByteBuffer(length)); 35 | Assert.Equal("length", ex.ParamName); 36 | } 37 | 38 | [StructLayout(LayoutKind.Explicit, Size = 512)] 39 | private struct BigStruct 40 | { 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Primitives/PointFTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Globalization; 6 | using System.Numerics; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | using Xunit; 10 | 11 | namespace SixLabors.Primitives.Tests 12 | { 13 | public class PointFTests 14 | { 15 | [Fact] 16 | public void CanReinterpretCastFromVector2() 17 | { 18 | var vector = new Vector2(1, 2); 19 | 20 | PointF point = Unsafe.As(ref vector); 21 | 22 | Assert.Equal(vector.X, point.X); 23 | Assert.Equal(vector.Y, point.Y); 24 | } 25 | 26 | [Fact] 27 | public void DefaultConstructorTest() 28 | { 29 | Assert.Equal(default, PointF.Empty); 30 | } 31 | 32 | [Theory] 33 | [InlineData(float.MaxValue, float.MinValue)] 34 | [InlineData(float.MinValue, float.MinValue)] 35 | [InlineData(float.MaxValue, float.MaxValue)] 36 | [InlineData(float.MinValue, float.MaxValue)] 37 | [InlineData(0.0, 0.0)] 38 | public void NonDefaultConstructorTest(float x, float y) 39 | { 40 | var p1 = new PointF(x, y); 41 | 42 | Assert.Equal(x, p1.X); 43 | Assert.Equal(y, p1.Y); 44 | } 45 | 46 | [Fact] 47 | public void IsEmptyDefaultsTest() 48 | { 49 | Assert.True(PointF.Empty.IsEmpty); 50 | Assert.True(default(PointF).IsEmpty); 51 | Assert.True(new PointF(0, 0).IsEmpty); 52 | } 53 | 54 | [Theory] 55 | [InlineData(float.MaxValue, float.MinValue)] 56 | [InlineData(float.MinValue, float.MinValue)] 57 | [InlineData(float.MaxValue, float.MaxValue)] 58 | public void IsEmptyRandomTest(float x, float y) 59 | { 60 | Assert.False(new PointF(x, y).IsEmpty); 61 | } 62 | 63 | [Theory] 64 | [InlineData(float.MaxValue, float.MinValue)] 65 | [InlineData(float.MinValue, float.MinValue)] 66 | [InlineData(float.MaxValue, float.MaxValue)] 67 | [InlineData(0, 0)] 68 | public void CoordinatesTest(float x, float y) 69 | { 70 | var p = new PointF(x, y); 71 | Assert.Equal(x, p.X); 72 | Assert.Equal(y, p.Y); 73 | 74 | p.X = 10; 75 | Assert.Equal(10, p.X); 76 | 77 | p.Y = -10.123f; 78 | Assert.Equal(-10.123, p.Y, 3); 79 | } 80 | 81 | [Theory] 82 | [InlineData(float.MaxValue, float.MinValue, int.MaxValue, int.MinValue)] 83 | [InlineData(float.MinValue, float.MaxValue, int.MinValue, int.MaxValue)] 84 | [InlineData(0, 0, 0, 0)] 85 | public void ArithmeticTestWithSize(float x, float y, int x1, int y1) 86 | { 87 | var p = new PointF(x, y); 88 | var s = new Size(x1, y1); 89 | 90 | var addExpected = new PointF(x + x1, y + y1); 91 | var subExpected = new PointF(x - x1, y - y1); 92 | Assert.Equal(addExpected, p + s); 93 | Assert.Equal(subExpected, p - s); 94 | Assert.Equal(addExpected, PointF.Add(p, s)); 95 | Assert.Equal(subExpected, PointF.Subtract(p, s)); 96 | } 97 | 98 | [Theory] 99 | [InlineData(float.MaxValue, float.MinValue)] 100 | [InlineData(float.MinValue, float.MaxValue)] 101 | [InlineData(0, 0)] 102 | public void ArithmeticTestWithSizeF(float x, float y) 103 | { 104 | var p = new PointF(x, y); 105 | var s = new SizeF(y, x); 106 | 107 | var addExpected = new PointF(x + y, y + x); 108 | var subExpected = new PointF(x - y, y - x); 109 | Assert.Equal(addExpected, p + s); 110 | Assert.Equal(subExpected, p - s); 111 | Assert.Equal(addExpected, PointF.Add(p, s)); 112 | Assert.Equal(subExpected, PointF.Subtract(p, s)); 113 | } 114 | 115 | [Fact] 116 | public void RotateTest() 117 | { 118 | var p = new PointF(13, 17); 119 | Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); 120 | 121 | var pout = PointF.Transform(p, matrix); 122 | 123 | Assert.Equal(new PointF(-2.82842732F, 21.2132034F), pout); 124 | } 125 | 126 | [Fact] 127 | public void SkewTest() 128 | { 129 | var p = new PointF(13, 17); 130 | Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, PointF.Empty); 131 | 132 | var pout = PointF.Transform(p, matrix); 133 | Assert.Equal(new PointF(30, 30), pout); 134 | } 135 | 136 | [Theory] 137 | [InlineData(float.MaxValue, float.MinValue)] 138 | [InlineData(float.MinValue, float.MaxValue)] 139 | [InlineData(float.MinValue, float.MinValue)] 140 | [InlineData(float.MaxValue, float.MaxValue)] 141 | [InlineData(0, 0)] 142 | public void EqualityTest(float x, float y) 143 | { 144 | var pLeft = new PointF(x, y); 145 | var pRight = new PointF(y, x); 146 | 147 | if (x == y) 148 | { 149 | Assert.True(pLeft == pRight); 150 | Assert.False(pLeft != pRight); 151 | Assert.True(pLeft.Equals(pRight)); 152 | Assert.True(pLeft.Equals((object)pRight)); 153 | Assert.Equal(pLeft.GetHashCode(), pRight.GetHashCode()); 154 | return; 155 | } 156 | 157 | Assert.True(pLeft != pRight); 158 | Assert.False(pLeft == pRight); 159 | Assert.False(pLeft.Equals(pRight)); 160 | Assert.False(pLeft.Equals((object)pRight)); 161 | } 162 | 163 | [Fact] 164 | public void EqualityTest_NotPointF() 165 | { 166 | var point = new PointF(0, 0); 167 | Assert.False(point.Equals(null)); 168 | Assert.False(point.Equals(0)); 169 | 170 | // If PointF implements IEquatable (e.g. in .NET Core), then structs that are implicitly 171 | // convertible to var can potentially be equal. 172 | // See https://github.com/dotnet/corefx/issues/5255. 173 | bool expectsImplicitCastToPointF = typeof(IEquatable).IsAssignableFrom(point.GetType()); 174 | Assert.Equal(expectsImplicitCastToPointF, point.Equals(new Point(0, 0))); 175 | 176 | Assert.False(point.Equals((object)new Point(0, 0))); // No implicit cast 177 | } 178 | 179 | [Fact] 180 | public void GetHashCodeTest() 181 | { 182 | var point = new PointF(10, 10); 183 | Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode()); 184 | Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode()); 185 | Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode()); 186 | } 187 | 188 | [Fact] 189 | public void ToStringTest() 190 | { 191 | var p = new PointF(5.1F, -5.123F); 192 | Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); 193 | } 194 | 195 | [Theory] 196 | [InlineData(float.MaxValue, float.MinValue)] 197 | [InlineData(float.MinValue, float.MinValue)] 198 | [InlineData(float.MaxValue, float.MaxValue)] 199 | [InlineData(0, 0)] 200 | public void DeconstructTest(float x, float y) 201 | { 202 | PointF p = new PointF(x, y); 203 | 204 | (float deconstructedX, float deconstructedY) = p; 205 | 206 | Assert.Equal(x, deconstructedX); 207 | Assert.Equal(y, deconstructedY); 208 | } 209 | } 210 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Primitives/PointTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Globalization; 6 | using System.Numerics; 7 | using Xunit; 8 | 9 | namespace SixLabors.Primitives.Tests 10 | { 11 | public class PointTests 12 | { 13 | [Fact] 14 | public void DefaultConstructorTest() 15 | { 16 | Assert.Equal(default, Point.Empty); 17 | } 18 | 19 | [Theory] 20 | [InlineData(int.MaxValue, int.MinValue)] 21 | [InlineData(int.MinValue, int.MinValue)] 22 | [InlineData(int.MaxValue, int.MaxValue)] 23 | [InlineData(0, 0)] 24 | public void NonDefaultConstructorTest(int x, int y) 25 | { 26 | var p1 = new Point(x, y); 27 | var p2 = new Point(new Size(x, y)); 28 | 29 | Assert.Equal(p1, p2); 30 | } 31 | 32 | [Theory] 33 | [InlineData(int.MaxValue)] 34 | [InlineData(int.MinValue)] 35 | [InlineData(0)] 36 | public void SingleIntConstructorTest(int x) 37 | { 38 | var p1 = new Point(x); 39 | var p2 = new Point(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); 40 | 41 | Assert.Equal(p1, p2); 42 | } 43 | 44 | [Fact] 45 | public void IsEmptyDefaultsTest() 46 | { 47 | Assert.True(Point.Empty.IsEmpty); 48 | Assert.True(default(Point).IsEmpty); 49 | Assert.True(new Point(0, 0).IsEmpty); 50 | } 51 | 52 | [Theory] 53 | [InlineData(int.MaxValue, int.MinValue)] 54 | [InlineData(int.MinValue, int.MinValue)] 55 | [InlineData(int.MaxValue, int.MaxValue)] 56 | public void IsEmptyRandomTest(int x, int y) 57 | { 58 | Assert.False(new Point(x, y).IsEmpty); 59 | } 60 | 61 | [Theory] 62 | [InlineData(int.MaxValue, int.MinValue)] 63 | [InlineData(int.MinValue, int.MinValue)] 64 | [InlineData(int.MaxValue, int.MaxValue)] 65 | [InlineData(0, 0)] 66 | public void CoordinatesTest(int x, int y) 67 | { 68 | var p = new Point(x, y); 69 | Assert.Equal(x, p.X); 70 | Assert.Equal(y, p.Y); 71 | } 72 | 73 | [Theory] 74 | [InlineData(int.MaxValue, int.MinValue)] 75 | [InlineData(int.MinValue, int.MinValue)] 76 | [InlineData(int.MaxValue, int.MaxValue)] 77 | [InlineData(0, 0)] 78 | public void PointFConversionTest(int x, int y) 79 | { 80 | PointF p = new Point(x, y); 81 | Assert.Equal(new PointF(x, y), p); 82 | } 83 | 84 | [Theory] 85 | [InlineData(int.MaxValue, int.MinValue)] 86 | [InlineData(int.MinValue, int.MinValue)] 87 | [InlineData(int.MaxValue, int.MaxValue)] 88 | [InlineData(0, 0)] 89 | public void SizeConversionTest(int x, int y) 90 | { 91 | var sz = (Size)new Point(x, y); 92 | Assert.Equal(new Size(x, y), sz); 93 | } 94 | 95 | [Theory] 96 | [InlineData(int.MaxValue, int.MinValue)] 97 | [InlineData(int.MinValue, int.MinValue)] 98 | [InlineData(int.MaxValue, int.MaxValue)] 99 | [InlineData(0, 0)] 100 | public void ArithmeticTest(int x, int y) 101 | { 102 | Point addExpected, subExpected, p = new Point(x, y); 103 | var s = new Size(y, x); 104 | 105 | unchecked 106 | { 107 | addExpected = new Point(x + y, y + x); 108 | subExpected = new Point(x - y, y - x); 109 | } 110 | 111 | Assert.Equal(addExpected, p + s); 112 | Assert.Equal(subExpected, p - s); 113 | Assert.Equal(addExpected, Point.Add(p, s)); 114 | Assert.Equal(subExpected, Point.Subtract(p, s)); 115 | } 116 | 117 | [Theory] 118 | [InlineData(float.MaxValue, float.MinValue)] 119 | [InlineData(float.MinValue, float.MinValue)] 120 | [InlineData(float.MaxValue, float.MaxValue)] 121 | [InlineData(0, 0)] 122 | public void PointFMathematicalTest(float x, float y) 123 | { 124 | var pf = new PointF(x, y); 125 | Point pCeiling, pTruncate, pRound; 126 | 127 | unchecked 128 | { 129 | pCeiling = new Point((int)MathF.Ceiling(x), (int)MathF.Ceiling(y)); 130 | pTruncate = new Point((int)x, (int)y); 131 | pRound = new Point((int)MathF.Round(x), (int)MathF.Round(y)); 132 | } 133 | 134 | Assert.Equal(pCeiling, Point.Ceiling(pf)); 135 | Assert.Equal(pRound, Point.Round(pf)); 136 | Assert.Equal(pTruncate, (Point)pf); 137 | } 138 | 139 | [Theory] 140 | [InlineData(int.MaxValue, int.MinValue)] 141 | [InlineData(int.MinValue, int.MinValue)] 142 | [InlineData(int.MaxValue, int.MaxValue)] 143 | [InlineData(0, 0)] 144 | public void OffsetTest(int x, int y) 145 | { 146 | var p1 = new Point(x, y); 147 | var p2 = new Point(y, x); 148 | 149 | p1.Offset(p2); 150 | 151 | Assert.Equal(unchecked(p2.X + p2.Y), p1.X); 152 | Assert.Equal(p1.X, p1.Y); 153 | 154 | p2.Offset(x, y); 155 | Assert.Equal(p1, p2); 156 | } 157 | 158 | [Fact] 159 | public void RotateTest() 160 | { 161 | var p = new Point(13, 17); 162 | Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, Point.Empty); 163 | 164 | var pout = Point.Transform(p, matrix); 165 | 166 | Assert.Equal(new Point(-3, 21), pout); 167 | } 168 | 169 | [Fact] 170 | public void SkewTest() 171 | { 172 | var p = new Point(13, 17); 173 | Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, Point.Empty); 174 | 175 | var pout = Point.Transform(p, matrix); 176 | Assert.Equal(new Point(30, 30), pout); 177 | } 178 | 179 | [Theory] 180 | [InlineData(int.MaxValue, int.MinValue)] 181 | [InlineData(int.MinValue, int.MinValue)] 182 | [InlineData(int.MaxValue, int.MaxValue)] 183 | [InlineData(0, 0)] 184 | public void EqualityTest(int x, int y) 185 | { 186 | var p1 = new Point(x, y); 187 | var p2 = new Point((x / 2) - 1, (y / 2) - 1); 188 | var p3 = new Point(x, y); 189 | 190 | Assert.True(p1 == p3); 191 | Assert.True(p1 != p2); 192 | Assert.True(p2 != p3); 193 | 194 | Assert.True(p1.Equals(p3)); 195 | Assert.False(p1.Equals(p2)); 196 | Assert.False(p2.Equals(p3)); 197 | 198 | Assert.True(p1.Equals((object)p3)); 199 | Assert.False(p1.Equals((object)p2)); 200 | Assert.False(p2.Equals((object)p3)); 201 | 202 | Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); 203 | } 204 | 205 | [Fact] 206 | public void EqualityTest_NotPoint() 207 | { 208 | var point = new Point(0, 0); 209 | Assert.False(point.Equals(null)); 210 | Assert.False(point.Equals(0)); 211 | Assert.False(point.Equals(new PointF(0, 0))); 212 | } 213 | 214 | [Fact] 215 | public void GetHashCodeTest() 216 | { 217 | var point = new Point(10, 10); 218 | Assert.Equal(point.GetHashCode(), new Point(10, 10).GetHashCode()); 219 | Assert.NotEqual(point.GetHashCode(), new Point(20, 10).GetHashCode()); 220 | Assert.NotEqual(point.GetHashCode(), new Point(10, 20).GetHashCode()); 221 | } 222 | 223 | [Theory] 224 | [InlineData(0, 0, 0, 0)] 225 | [InlineData(1, -2, 3, -4)] 226 | public void ConversionTest(int x, int y, int width, int height) 227 | { 228 | var rect = new Rectangle(x, y, width, height); 229 | RectangleF rectF = rect; 230 | Assert.Equal(x, rectF.X); 231 | Assert.Equal(y, rectF.Y); 232 | Assert.Equal(width, rectF.Width); 233 | Assert.Equal(height, rectF.Height); 234 | } 235 | 236 | [Fact] 237 | public void ToStringTest() 238 | { 239 | var p = new Point(5, -5); 240 | Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Point [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); 241 | } 242 | 243 | [Theory] 244 | [InlineData(int.MaxValue, int.MinValue)] 245 | [InlineData(int.MinValue, int.MinValue)] 246 | [InlineData(int.MaxValue, int.MaxValue)] 247 | [InlineData(0, 0)] 248 | public void DeconstructTest(int x, int y) 249 | { 250 | Point p = new Point(x, y); 251 | 252 | (int deconstructedX, int deconstructedY) = p; 253 | 254 | Assert.Equal(x, deconstructedX); 255 | Assert.Equal(y, deconstructedY); 256 | } 257 | } 258 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Primitives/RectangleFTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Globalization; 6 | using System.Reflection; 7 | using Xunit; 8 | 9 | namespace SixLabors.Primitives.Tests 10 | { 11 | /// 12 | /// Tests the struct. 13 | /// 14 | public class RectangleFTests 15 | { 16 | [Fact] 17 | public void DefaultConstructorTest() 18 | { 19 | Assert.Equal(default, RectangleF.Empty); 20 | } 21 | 22 | [Theory] 23 | [InlineData(0, 0, 0, 0)] 24 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 25 | [InlineData(float.MaxValue, 0, 0, float.MaxValue)] 26 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 27 | public void NonDefaultConstructorTest(float x, float y, float width, float height) 28 | { 29 | var rect1 = new RectangleF(x, y, width, height); 30 | var p = new PointF(x, y); 31 | var s = new SizeF(width, height); 32 | var rect2 = new RectangleF(p, s); 33 | 34 | Assert.Equal(rect1, rect2); 35 | } 36 | 37 | [Theory] 38 | [InlineData(0, 0, 0, 0)] 39 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 40 | [InlineData(float.MaxValue, 0, 0, float.MaxValue)] 41 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 42 | public void FromLTRBTest(float left, float top, float right, float bottom) 43 | { 44 | var expected = new RectangleF(left, top, right - left, bottom - top); 45 | var actual = RectangleF.FromLTRB(left, top, right, bottom); 46 | 47 | Assert.Equal(expected, actual); 48 | } 49 | 50 | [Theory] 51 | [InlineData(0, 0, 0, 0)] 52 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 53 | [InlineData(float.MaxValue, 0, 0, float.MaxValue)] 54 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 55 | public void DimensionsTest(float x, float y, float width, float height) 56 | { 57 | var rect = new RectangleF(x, y, width, height); 58 | var p = new PointF(x, y); 59 | var s = new SizeF(width, height); 60 | 61 | Assert.Equal(p, rect.Location); 62 | Assert.Equal(s, rect.Size); 63 | Assert.Equal(x, rect.X); 64 | Assert.Equal(y, rect.Y); 65 | Assert.Equal(width, rect.Width); 66 | Assert.Equal(height, rect.Height); 67 | Assert.Equal(x, rect.Left); 68 | Assert.Equal(y, rect.Top); 69 | Assert.Equal(x + width, rect.Right); 70 | Assert.Equal(y + height, rect.Bottom); 71 | } 72 | 73 | [Fact] 74 | public void IsEmptyTest() 75 | { 76 | Assert.True(RectangleF.Empty.IsEmpty); 77 | Assert.True(default(RectangleF).IsEmpty); 78 | Assert.True(new RectangleF(1, -2, -10, 10).IsEmpty); 79 | Assert.True(new RectangleF(1, -2, 10, -10).IsEmpty); 80 | Assert.True(new RectangleF(1, -2, 0, 0).IsEmpty); 81 | 82 | Assert.False(new RectangleF(0, 0, 10, 10).IsEmpty); 83 | } 84 | 85 | [Theory] 86 | [InlineData(0, 0)] 87 | [InlineData(float.MaxValue, float.MinValue)] 88 | public void LocationSetTest(float x, float y) 89 | { 90 | var point = new PointF(x, y); 91 | var rect = new RectangleF(10, 10, 10, 10) { Location = point }; 92 | Assert.Equal(point, rect.Location); 93 | Assert.Equal(point.X, rect.X); 94 | Assert.Equal(point.Y, rect.Y); 95 | } 96 | 97 | [Theory] 98 | [InlineData(0, 0)] 99 | [InlineData(float.MaxValue, float.MinValue)] 100 | public void SizeSetTest(float x, float y) 101 | { 102 | var size = new SizeF(x, y); 103 | var rect = new RectangleF(10, 10, 10, 10) { Size = size }; 104 | Assert.Equal(size, rect.Size); 105 | Assert.Equal(size.Width, rect.Width); 106 | Assert.Equal(size.Height, rect.Height); 107 | } 108 | 109 | [Theory] 110 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 111 | [InlineData(float.MaxValue, 0, 0, float.MaxValue)] 112 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 113 | public void EqualityTest(float x, float y, float width, float height) 114 | { 115 | var rect1 = new RectangleF(x, y, width, height); 116 | var rect2 = new RectangleF(width, height, x, y); 117 | 118 | Assert.True(rect1 != rect2); 119 | Assert.False(rect1 == rect2); 120 | Assert.False(rect1.Equals(rect2)); 121 | Assert.False(rect1.Equals((object)rect2)); 122 | } 123 | 124 | [Fact] 125 | public void EqualityTestNotRectangleF() 126 | { 127 | var rectangle = new RectangleF(0, 0, 0, 0); 128 | Assert.False(rectangle.Equals(null)); 129 | Assert.False(rectangle.Equals(0)); 130 | 131 | // If RectangleF implements IEquatable (e.g. in .NET Core), then classes that are implicitly 132 | // convertible to RectangleF can potentially be equal. 133 | // See https://github.com/dotnet/corefx/issues/5255. 134 | bool expectsImplicitCastToRectangleF = typeof(IEquatable).IsAssignableFrom(rectangle.GetType()); 135 | Assert.Equal(expectsImplicitCastToRectangleF, rectangle.Equals(new Rectangle(0, 0, 0, 0))); 136 | 137 | Assert.False(rectangle.Equals((object)new Rectangle(0, 0, 0, 0))); // No implicit cast 138 | } 139 | 140 | [Fact] 141 | public void GetHashCodeTest() 142 | { 143 | var rect1 = new RectangleF(10, 10, 10, 10); 144 | var rect2 = new RectangleF(10, 10, 10, 10); 145 | Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); 146 | Assert.NotEqual(rect1.GetHashCode(), new RectangleF(20, 10, 10, 10).GetHashCode()); 147 | Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 20, 10, 10).GetHashCode()); 148 | Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 20, 10).GetHashCode()); 149 | Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 10, 20).GetHashCode()); 150 | } 151 | 152 | [Theory] 153 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 154 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 155 | public void ContainsTest(float x, float y, float width, float height) 156 | { 157 | var rect = new RectangleF(x, y, width, height); 158 | float x1 = (x + width) / 2; 159 | float y1 = (y + height) / 2; 160 | var p = new PointF(x1, y1); 161 | var r = new RectangleF(x1, y1, width / 2, height / 2); 162 | 163 | Assert.False(rect.Contains(x1, y1)); 164 | Assert.False(rect.Contains(p)); 165 | Assert.False(rect.Contains(r)); 166 | } 167 | 168 | [Theory] 169 | [InlineData(0, 0, 0, 0)] 170 | [InlineData(float.MaxValue / 2, float.MinValue / 2, float.MinValue / 2, float.MaxValue / 2)] 171 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 172 | public void InflateTest(float x, float y, float width, float height) 173 | { 174 | var rect = new RectangleF(x, y, width, height); 175 | var inflatedRect = new RectangleF(x - width, y - height, width + (2 * width), height + (2 * height)); 176 | 177 | rect.Inflate(width, height); 178 | Assert.Equal(inflatedRect, rect); 179 | 180 | var s = new SizeF(x, y); 181 | inflatedRect = RectangleF.Inflate(rect, x, y); 182 | 183 | rect.Inflate(s); 184 | Assert.Equal(inflatedRect, rect); 185 | } 186 | 187 | [Theory] 188 | [InlineData(float.MaxValue, float.MinValue, float.MaxValue / 2, float.MinValue / 2)] 189 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 190 | public void IntersectTest(float x, float y, float width, float height) 191 | { 192 | var rect1 = new RectangleF(x, y, width, height); 193 | var rect2 = new RectangleF(y, x, width, height); 194 | var expectedRect = RectangleF.Intersect(rect1, rect2); 195 | rect1.Intersect(rect2); 196 | Assert.Equal(expectedRect, rect1); 197 | Assert.False(rect1.IntersectsWith(expectedRect)); 198 | } 199 | 200 | [Fact] 201 | public void IntersectIntersectingRectsTest() 202 | { 203 | var rect1 = new RectangleF(0, 0, 5, 5); 204 | var rect2 = new RectangleF(1, 1, 3, 3); 205 | var expected = new RectangleF(1, 1, 3, 3); 206 | 207 | Assert.Equal(expected, RectangleF.Intersect(rect1, rect2)); 208 | } 209 | 210 | [Theory] 211 | [InlineData(0, 0, 0, 0)] 212 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 213 | [InlineData(float.MaxValue, 0, 0, float.MaxValue)] 214 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 215 | public void UnionTest(float x, float y, float width, float height) 216 | { 217 | var a = new RectangleF(x, y, width, height); 218 | var b = new RectangleF(width, height, x, y); 219 | 220 | float x1 = Math.Min(a.X, b.X); 221 | float x2 = Math.Max(a.X + a.Width, b.X + b.Width); 222 | float y1 = Math.Min(a.Y, b.Y); 223 | float y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); 224 | 225 | var expectedRectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1); 226 | 227 | Assert.Equal(expectedRectangle, RectangleF.Union(a, b)); 228 | } 229 | 230 | [Theory] 231 | [InlineData(0, 0, 0, 0)] 232 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 233 | [InlineData(float.MaxValue, 0, 0, float.MaxValue)] 234 | [InlineData(0, float.MinValue, float.MaxValue, 0)] 235 | public void OffsetTest(float x, float y, float width, float height) 236 | { 237 | var r1 = new RectangleF(x, y, width, height); 238 | var expectedRect = new RectangleF(x + width, y + height, width, height); 239 | var p = new PointF(width, height); 240 | 241 | r1.Offset(p); 242 | Assert.Equal(expectedRect, r1); 243 | 244 | expectedRect.Offset(p); 245 | r1.Offset(width, height); 246 | Assert.Equal(expectedRect, r1); 247 | } 248 | 249 | [Fact] 250 | public void ToStringTest() 251 | { 252 | var r = new RectangleF(5, 5.1F, 1.3F, 1); 253 | Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); 254 | } 255 | 256 | [Theory] 257 | [InlineData(float.MinValue, float.MaxValue, float.MaxValue, float.MaxValue)] 258 | [InlineData(float.MinValue, float.MaxValue, float.MaxValue, float.MinValue)] 259 | [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)] 260 | [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MinValue)] 261 | [InlineData(float.MinValue, float.MinValue, float.MaxValue, float.MaxValue)] 262 | [InlineData(float.MinValue, float.MinValue, float.MaxValue, float.MinValue)] 263 | [InlineData(float.MinValue, float.MinValue, float.MinValue, float.MaxValue)] 264 | [InlineData(float.MinValue, float.MinValue, float.MinValue, float.MinValue)] 265 | [InlineData(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue)] 266 | [InlineData(float.MaxValue, float.MaxValue, float.MaxValue, float.MinValue)] 267 | [InlineData(float.MaxValue, float.MaxValue, float.MinValue, float.MaxValue)] 268 | [InlineData(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue)] 269 | [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MaxValue)] 270 | [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)] 271 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] 272 | [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MinValue)] 273 | [InlineData(0, 0, 0, 0)] 274 | public void DeconstructTest(float x, float y, float width, float height) 275 | { 276 | RectangleF r = new RectangleF(x, y, width, height); 277 | 278 | (float dx, float dy, float dw, float dh) = r; 279 | 280 | Assert.Equal(x, dx); 281 | Assert.Equal(y, dy); 282 | Assert.Equal(width, dw); 283 | Assert.Equal(height, dh); 284 | } 285 | } 286 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Primitives/SizeFTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Globalization; 6 | using System.Reflection; 7 | using Xunit; 8 | 9 | namespace SixLabors.Primitives.Tests 10 | { 11 | public class SizeFTests 12 | { 13 | [Fact] 14 | public void DefaultConstructorTest() 15 | { 16 | Assert.Equal(default, SizeF.Empty); 17 | } 18 | 19 | [Theory] 20 | [InlineData(float.MaxValue, float.MinValue)] 21 | [InlineData(float.MinValue, float.MinValue)] 22 | [InlineData(float.MaxValue, float.MaxValue)] 23 | [InlineData(0, 0)] 24 | public void NonDefaultConstructorAndDimensionsTest(float width, float height) 25 | { 26 | var s1 = new SizeF(width, height); 27 | var p1 = new PointF(width, height); 28 | var s2 = new SizeF(s1); 29 | 30 | Assert.Equal(s1, s2); 31 | Assert.Equal(s1, new SizeF(p1)); 32 | Assert.Equal(s2, new SizeF(p1)); 33 | 34 | Assert.Equal(width, s1.Width); 35 | Assert.Equal(height, s1.Height); 36 | 37 | s1.Width = 10; 38 | Assert.Equal(10, s1.Width); 39 | 40 | s1.Height = -10.123f; 41 | Assert.Equal(-10.123, s1.Height, 3); 42 | } 43 | 44 | [Fact] 45 | public void IsEmptyDefaultsTest() 46 | { 47 | Assert.True(SizeF.Empty.IsEmpty); 48 | Assert.True(default(SizeF).IsEmpty); 49 | Assert.True(new SizeF(0, 0).IsEmpty); 50 | } 51 | 52 | [Theory] 53 | [InlineData(float.MaxValue, float.MinValue)] 54 | [InlineData(float.MinValue, float.MinValue)] 55 | [InlineData(float.MaxValue, float.MaxValue)] 56 | public void IsEmptyRandomTest(float width, float height) 57 | { 58 | Assert.False(new SizeF(width, height).IsEmpty); 59 | } 60 | 61 | [Theory] 62 | [InlineData(float.MaxValue, float.MinValue)] 63 | [InlineData(float.MinValue, float.MinValue)] 64 | [InlineData(float.MaxValue, float.MaxValue)] 65 | [InlineData(0, 0)] 66 | public void ArithmeticTest(float width, float height) 67 | { 68 | var s1 = new SizeF(width, height); 69 | var s2 = new SizeF(height, width); 70 | var addExpected = new SizeF(width + height, width + height); 71 | var subExpected = new SizeF(width - height, height - width); 72 | 73 | Assert.Equal(addExpected, s1 + s2); 74 | Assert.Equal(addExpected, SizeF.Add(s1, s2)); 75 | 76 | Assert.Equal(subExpected, s1 - s2); 77 | Assert.Equal(subExpected, SizeF.Subtract(s1, s2)); 78 | } 79 | 80 | [Theory] 81 | [InlineData(float.MaxValue, float.MinValue)] 82 | [InlineData(float.MinValue, float.MinValue)] 83 | [InlineData(float.MaxValue, float.MaxValue)] 84 | [InlineData(0, 0)] 85 | public void EqualityTest(float width, float height) 86 | { 87 | var sLeft = new SizeF(width, height); 88 | var sRight = new SizeF(height, width); 89 | 90 | if (width == height) 91 | { 92 | Assert.True(sLeft == sRight); 93 | Assert.False(sLeft != sRight); 94 | Assert.True(sLeft.Equals(sRight)); 95 | Assert.True(sLeft.Equals((object)sRight)); 96 | Assert.Equal(sLeft.GetHashCode(), sRight.GetHashCode()); 97 | return; 98 | } 99 | 100 | Assert.True(sLeft != sRight); 101 | Assert.False(sLeft == sRight); 102 | Assert.False(sLeft.Equals(sRight)); 103 | Assert.False(sLeft.Equals((object)sRight)); 104 | } 105 | 106 | [Fact] 107 | public void EqualityTest_NotSizeF() 108 | { 109 | var size = new SizeF(0, 0); 110 | Assert.False(size.Equals(null)); 111 | Assert.False(size.Equals(0)); 112 | 113 | // If SizeF implements IEquatable (e.g in .NET Core), then classes that are implicitly 114 | // convertible to SizeF can potentially be equal. 115 | // See https://github.com/dotnet/corefx/issues/5255. 116 | bool expectsImplicitCastToSizeF = typeof(IEquatable).IsAssignableFrom(size.GetType()); 117 | Assert.Equal(expectsImplicitCastToSizeF, size.Equals(new Size(0, 0))); 118 | 119 | Assert.False(size.Equals((object)new Size(0, 0))); // No implicit cast 120 | } 121 | 122 | [Fact] 123 | public void GetHashCodeTest() 124 | { 125 | var size = new SizeF(10, 10); 126 | Assert.Equal(size.GetHashCode(), new SizeF(10, 10).GetHashCode()); 127 | Assert.NotEqual(size.GetHashCode(), new SizeF(20, 10).GetHashCode()); 128 | Assert.NotEqual(size.GetHashCode(), new SizeF(10, 20).GetHashCode()); 129 | } 130 | 131 | [Theory] 132 | [InlineData(float.MaxValue, float.MinValue)] 133 | [InlineData(float.MinValue, float.MinValue)] 134 | [InlineData(float.MaxValue, float.MaxValue)] 135 | [InlineData(0, 0)] 136 | public void ConversionTest(float width, float height) 137 | { 138 | var s1 = new SizeF(width, height); 139 | var p1 = (PointF)s1; 140 | var s2 = new Size(unchecked((int)width), unchecked((int)height)); 141 | 142 | Assert.Equal(new PointF(width, height), p1); 143 | Assert.Equal(p1, (PointF)s1); 144 | Assert.Equal(s2, (Size)s1); 145 | } 146 | 147 | [Fact] 148 | public void ToStringTest() 149 | { 150 | var sz = new SizeF(10, 5); 151 | Assert.Equal(string.Format(CultureInfo.CurrentCulture, "SizeF [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); 152 | } 153 | 154 | [Theory] 155 | [InlineData(1000.234f, 0.0f)] 156 | [InlineData(1000.234f, 1.0f)] 157 | [InlineData(1000.234f, 2400.933f)] 158 | [InlineData(1000.234f, float.MaxValue)] 159 | [InlineData(1000.234f, -1.0f)] 160 | [InlineData(1000.234f, -2400.933f)] 161 | [InlineData(1000.234f, float.MinValue)] 162 | [InlineData(float.MaxValue, 0.0f)] 163 | [InlineData(float.MaxValue, 1.0f)] 164 | [InlineData(float.MaxValue, 2400.933f)] 165 | [InlineData(float.MaxValue, float.MaxValue)] 166 | [InlineData(float.MaxValue, -1.0f)] 167 | [InlineData(float.MaxValue, -2400.933f)] 168 | [InlineData(float.MaxValue, float.MinValue)] 169 | [InlineData(float.MinValue, 0.0f)] 170 | [InlineData(float.MinValue, 1.0f)] 171 | [InlineData(float.MinValue, 2400.933f)] 172 | [InlineData(float.MinValue, float.MaxValue)] 173 | [InlineData(float.MinValue, -1.0f)] 174 | [InlineData(float.MinValue, -2400.933f)] 175 | [InlineData(float.MinValue, float.MinValue)] 176 | public void MultiplicationTest(float dimension, float multiplier) 177 | { 178 | SizeF sz1 = new SizeF(dimension, dimension); 179 | SizeF mulExpected; 180 | 181 | mulExpected = new SizeF(dimension * multiplier, dimension * multiplier); 182 | 183 | Assert.Equal(mulExpected, sz1 * multiplier); 184 | Assert.Equal(mulExpected, multiplier * sz1); 185 | } 186 | 187 | [Theory] 188 | [InlineData(1111.1111f, 2222.2222f, 3333.3333f)] 189 | public void MultiplicationTestWidthHeightMultiplier(float width, float height, float multiplier) 190 | { 191 | SizeF sz1 = new SizeF(width, height); 192 | SizeF mulExpected; 193 | 194 | mulExpected = new SizeF(width * multiplier, height * multiplier); 195 | 196 | Assert.Equal(mulExpected, sz1 * multiplier); 197 | Assert.Equal(mulExpected, multiplier * sz1); 198 | } 199 | 200 | [Theory] 201 | [InlineData(0.0f, 1.0f)] 202 | [InlineData(1.0f, 1.0f)] 203 | [InlineData(-1.0f, 1.0f)] 204 | [InlineData(1.0f, -1.0f)] 205 | [InlineData(-1.0f, -1.0f)] 206 | [InlineData(float.MaxValue, float.MaxValue)] 207 | [InlineData(float.MaxValue, float.MinValue)] 208 | [InlineData(float.MinValue, float.MaxValue)] 209 | [InlineData(float.MinValue, float.MinValue)] 210 | [InlineData(float.MaxValue, 1.0f)] 211 | [InlineData(float.MinValue, 1.0f)] 212 | [InlineData(float.MaxValue, -1.0f)] 213 | [InlineData(float.MinValue, -1.0f)] 214 | [InlineData(float.MinValue, 0.0f)] 215 | [InlineData(1.0f, float.MinValue)] 216 | [InlineData(1.0f, float.MaxValue)] 217 | [InlineData(-1.0f, float.MinValue)] 218 | [InlineData(-1.0f, float.MaxValue)] 219 | public void DivideTestSizeFloat(float dimension, float divisor) 220 | { 221 | SizeF size = new SizeF(dimension, dimension); 222 | SizeF expected = new SizeF(dimension / divisor, dimension / divisor); 223 | Assert.Equal(expected, size / divisor); 224 | } 225 | 226 | [Theory] 227 | [InlineData(-111.111f, 222.222f, 333.333f)] 228 | public void DivideTestSizeFloatWidthHeightDivisor(float width, float height, float divisor) 229 | { 230 | SizeF size = new SizeF(width, height); 231 | SizeF expected = new SizeF(width / divisor, height / divisor); 232 | Assert.Equal(expected, size / divisor); 233 | } 234 | 235 | [Theory] 236 | [InlineData(float.MaxValue, float.MinValue)] 237 | [InlineData(float.MinValue, float.MinValue)] 238 | [InlineData(float.MaxValue, float.MaxValue)] 239 | [InlineData(0, 0)] 240 | public void DeconstructTest(float width, float height) 241 | { 242 | SizeF s = new SizeF(width, height); 243 | 244 | (float deconstructedWidth, float deconstructedHeight) = s; 245 | 246 | Assert.Equal(width, deconstructedWidth); 247 | Assert.Equal(height, deconstructedHeight); 248 | } 249 | } 250 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/Primitives/SizeTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | using System.Globalization; 6 | using Xunit; 7 | 8 | namespace SixLabors.Primitives.Tests 9 | { 10 | /// 11 | /// Tests the struct. 12 | /// 13 | public class SizeTests 14 | { 15 | [Fact] 16 | public void DefaultConstructorTest() 17 | { 18 | Assert.Equal(default, Size.Empty); 19 | } 20 | 21 | [Theory] 22 | [InlineData(int.MaxValue, int.MinValue)] 23 | [InlineData(int.MinValue, int.MinValue)] 24 | [InlineData(int.MaxValue, int.MaxValue)] 25 | [InlineData(0, 0)] 26 | public void NonDefaultConstructorTest(int width, int height) 27 | { 28 | var s1 = new Size(width, height); 29 | var s2 = new Size(new Point(width, height)); 30 | 31 | Assert.Equal(s1, s2); 32 | 33 | s1.Width = 10; 34 | Assert.Equal(10, s1.Width); 35 | 36 | s1.Height = -10; 37 | Assert.Equal(-10, s1.Height); 38 | } 39 | 40 | [Fact] 41 | public void IsEmptyDefaultsTest() 42 | { 43 | Assert.True(Size.Empty.IsEmpty); 44 | Assert.True(default(Size).IsEmpty); 45 | Assert.True(new Size(0, 0).IsEmpty); 46 | } 47 | 48 | [Theory] 49 | [InlineData(int.MaxValue, int.MinValue)] 50 | [InlineData(int.MinValue, int.MinValue)] 51 | [InlineData(int.MaxValue, int.MaxValue)] 52 | public void IsEmptyRandomTest(int width, int height) 53 | { 54 | Assert.False(new Size(width, height).IsEmpty); 55 | } 56 | 57 | [Theory] 58 | [InlineData(int.MaxValue, int.MinValue)] 59 | [InlineData(int.MinValue, int.MinValue)] 60 | [InlineData(int.MaxValue, int.MaxValue)] 61 | [InlineData(0, 0)] 62 | public void DimensionsTest(int width, int height) 63 | { 64 | var p = new Size(width, height); 65 | Assert.Equal(width, p.Width); 66 | Assert.Equal(height, p.Height); 67 | } 68 | 69 | [Theory] 70 | [InlineData(int.MaxValue, int.MinValue)] 71 | [InlineData(int.MinValue, int.MinValue)] 72 | [InlineData(int.MaxValue, int.MaxValue)] 73 | [InlineData(0, 0)] 74 | public void PointFConversionTest(int width, int height) 75 | { 76 | SizeF sz = new Size(width, height); 77 | Assert.Equal(new SizeF(width, height), sz); 78 | } 79 | 80 | [Theory] 81 | [InlineData(int.MaxValue, int.MinValue)] 82 | [InlineData(int.MinValue, int.MinValue)] 83 | [InlineData(int.MaxValue, int.MaxValue)] 84 | [InlineData(0, 0)] 85 | public void SizeConversionTest(int width, int height) 86 | { 87 | var sz = (Point)new Size(width, height); 88 | Assert.Equal(new Point(width, height), sz); 89 | } 90 | 91 | [Theory] 92 | [InlineData(int.MaxValue, int.MinValue)] 93 | [InlineData(int.MinValue, int.MinValue)] 94 | [InlineData(int.MaxValue, int.MaxValue)] 95 | [InlineData(0, 0)] 96 | public void ArithmeticTest(int width, int height) 97 | { 98 | var sz1 = new Size(width, height); 99 | var sz2 = new Size(height, width); 100 | Size addExpected, subExpected; 101 | 102 | unchecked 103 | { 104 | addExpected = new Size(width + height, height + width); 105 | subExpected = new Size(width - height, height - width); 106 | } 107 | 108 | Assert.Equal(addExpected, sz1 + sz2); 109 | Assert.Equal(subExpected, sz1 - sz2); 110 | Assert.Equal(addExpected, Size.Add(sz1, sz2)); 111 | Assert.Equal(subExpected, Size.Subtract(sz1, sz2)); 112 | } 113 | 114 | [Theory] 115 | [InlineData(float.MaxValue, float.MinValue)] 116 | [InlineData(float.MinValue, float.MinValue)] 117 | [InlineData(float.MaxValue, float.MaxValue)] 118 | [InlineData(0, 0)] 119 | public void PointFMathematicalTest(float width, float height) 120 | { 121 | var szF = new SizeF(width, height); 122 | Size pCeiling, pTruncate, pRound; 123 | 124 | unchecked 125 | { 126 | pCeiling = new Size((int)MathF.Ceiling(width), (int)MathF.Ceiling(height)); 127 | pTruncate = new Size((int)width, (int)height); 128 | pRound = new Size((int)MathF.Round(width), (int)MathF.Round(height)); 129 | } 130 | 131 | Assert.Equal(pCeiling, Size.Ceiling(szF)); 132 | Assert.Equal(pRound, Size.Round(szF)); 133 | Assert.Equal(pTruncate, (Size)szF); 134 | } 135 | 136 | [Theory] 137 | [InlineData(int.MaxValue, int.MinValue)] 138 | [InlineData(int.MinValue, int.MinValue)] 139 | [InlineData(int.MaxValue, int.MaxValue)] 140 | [InlineData(0, 0)] 141 | public void EqualityTest(int width, int height) 142 | { 143 | var p1 = new Size(width, height); 144 | var p2 = new Size(unchecked(width - 1), unchecked(height - 1)); 145 | var p3 = new Size(width, height); 146 | 147 | Assert.True(p1 == p3); 148 | Assert.True(p1 != p2); 149 | Assert.True(p2 != p3); 150 | 151 | Assert.True(p1.Equals(p3)); 152 | Assert.False(p1.Equals(p2)); 153 | Assert.False(p2.Equals(p3)); 154 | 155 | Assert.True(p1.Equals((object)p3)); 156 | Assert.False(p1.Equals((object)p2)); 157 | Assert.False(p2.Equals((object)p3)); 158 | 159 | Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); 160 | } 161 | 162 | [Fact] 163 | public void EqualityTest_NotSize() 164 | { 165 | var size = new Size(0, 0); 166 | Assert.False(size.Equals(null)); 167 | Assert.False(size.Equals(0)); 168 | Assert.False(size.Equals(new SizeF(0, 0))); 169 | } 170 | 171 | [Fact] 172 | public void GetHashCodeTest() 173 | { 174 | var size = new Size(10, 10); 175 | Assert.Equal(size.GetHashCode(), new Size(10, 10).GetHashCode()); 176 | Assert.NotEqual(size.GetHashCode(), new Size(20, 10).GetHashCode()); 177 | Assert.NotEqual(size.GetHashCode(), new Size(10, 20).GetHashCode()); 178 | } 179 | 180 | [Fact] 181 | public void ToStringTest() 182 | { 183 | var sz = new Size(10, 5); 184 | Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Size [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); 185 | } 186 | 187 | [Theory] 188 | [InlineData(1000, 0)] 189 | [InlineData(1000, 1)] 190 | [InlineData(1000, 2400)] 191 | [InlineData(1000, int.MaxValue)] 192 | [InlineData(1000, -1)] 193 | [InlineData(1000, -2400)] 194 | [InlineData(1000, int.MinValue)] 195 | [InlineData(int.MaxValue, 0)] 196 | [InlineData(int.MaxValue, 1)] 197 | [InlineData(int.MaxValue, 2400)] 198 | [InlineData(int.MaxValue, int.MaxValue)] 199 | [InlineData(int.MaxValue, -1)] 200 | [InlineData(int.MaxValue, -2400)] 201 | [InlineData(int.MaxValue, int.MinValue)] 202 | [InlineData(int.MinValue, 0)] 203 | [InlineData(int.MinValue, 1)] 204 | [InlineData(int.MinValue, 2400)] 205 | [InlineData(int.MinValue, int.MaxValue)] 206 | [InlineData(int.MinValue, -1)] 207 | [InlineData(int.MinValue, -2400)] 208 | [InlineData(int.MinValue, int.MinValue)] 209 | public void MultiplicationTestSizeInt(int dimension, int multiplier) 210 | { 211 | Size sz1 = new Size(dimension, dimension); 212 | Size mulExpected; 213 | 214 | unchecked 215 | { 216 | mulExpected = new Size(dimension * multiplier, dimension * multiplier); 217 | } 218 | 219 | Assert.Equal(mulExpected, sz1 * multiplier); 220 | Assert.Equal(mulExpected, multiplier * sz1); 221 | } 222 | 223 | [Theory] 224 | [InlineData(1000, 2000, 3000)] 225 | public void MultiplicationTestSizeIntWidthHeightMultiplier(int width, int height, int multiplier) 226 | { 227 | Size sz1 = new Size(width, height); 228 | Size mulExpected; 229 | 230 | unchecked 231 | { 232 | mulExpected = new Size(width * multiplier, height * multiplier); 233 | } 234 | 235 | Assert.Equal(mulExpected, sz1 * multiplier); 236 | Assert.Equal(mulExpected, multiplier * sz1); 237 | } 238 | 239 | [Theory] 240 | [InlineData(1000, 0.0f)] 241 | [InlineData(1000, 1.0f)] 242 | [InlineData(1000, 2400.933f)] 243 | [InlineData(1000, float.MaxValue)] 244 | [InlineData(1000, -1.0f)] 245 | [InlineData(1000, -2400.933f)] 246 | [InlineData(1000, float.MinValue)] 247 | [InlineData(int.MaxValue, 0.0f)] 248 | [InlineData(int.MaxValue, 1.0f)] 249 | [InlineData(int.MaxValue, 2400.933f)] 250 | [InlineData(int.MaxValue, float.MaxValue)] 251 | [InlineData(int.MaxValue, -1.0f)] 252 | [InlineData(int.MaxValue, -2400.933f)] 253 | [InlineData(int.MaxValue, float.MinValue)] 254 | [InlineData(int.MinValue, 0.0f)] 255 | [InlineData(int.MinValue, 1.0f)] 256 | [InlineData(int.MinValue, 2400.933f)] 257 | [InlineData(int.MinValue, float.MaxValue)] 258 | [InlineData(int.MinValue, -1.0f)] 259 | [InlineData(int.MinValue, -2400.933f)] 260 | [InlineData(int.MinValue, float.MinValue)] 261 | public void MultiplicationTestSizeFloat(int dimension, float multiplier) 262 | { 263 | Size sz1 = new Size(dimension, dimension); 264 | SizeF mulExpected; 265 | 266 | mulExpected = new SizeF(dimension * multiplier, dimension * multiplier); 267 | 268 | Assert.Equal(mulExpected, sz1 * multiplier); 269 | Assert.Equal(mulExpected, multiplier * sz1); 270 | } 271 | 272 | [Theory] 273 | [InlineData(1000, 2000, 30.33f)] 274 | public void MultiplicationTestSizeFloatWidthHeightMultiplier(int width, int height, float multiplier) 275 | { 276 | Size sz1 = new Size(width, height); 277 | SizeF mulExpected; 278 | 279 | mulExpected = new SizeF(width * multiplier, height * multiplier); 280 | 281 | Assert.Equal(mulExpected, sz1 * multiplier); 282 | Assert.Equal(mulExpected, multiplier * sz1); 283 | } 284 | 285 | [Fact] 286 | public void DivideByZeroChecks() 287 | { 288 | Size size = new Size(100, 100); 289 | Assert.Throws(() => size / 0); 290 | 291 | SizeF expectedSizeF = new SizeF(float.PositiveInfinity, float.PositiveInfinity); 292 | Assert.Equal(expectedSizeF, size / 0.0f); 293 | } 294 | 295 | [Theory] 296 | [InlineData(0, 1)] 297 | [InlineData(1, 1)] 298 | [InlineData(-1, 1)] 299 | [InlineData(1, -1)] 300 | [InlineData(-1, -1)] 301 | [InlineData(int.MaxValue, int.MaxValue)] 302 | [InlineData(int.MaxValue, int.MinValue)] 303 | [InlineData(int.MinValue, int.MaxValue)] 304 | [InlineData(int.MinValue, int.MinValue)] 305 | [InlineData(int.MaxValue, 1)] 306 | [InlineData(int.MinValue, 1)] 307 | [InlineData(int.MaxValue, -1)] 308 | public void DivideTestSizeInt(int dimension, int divisor) 309 | { 310 | Size size = new Size(dimension, dimension); 311 | Size expected; 312 | 313 | expected = new Size(dimension / divisor, dimension / divisor); 314 | 315 | Assert.Equal(expected, size / divisor); 316 | } 317 | 318 | [Theory] 319 | [InlineData(1111, 2222, 3333)] 320 | public void DivideTestSizeIntWidthHeightDivisor(int width, int height, int divisor) 321 | { 322 | Size size = new Size(width, height); 323 | Size expected; 324 | 325 | expected = new Size(width / divisor, height / divisor); 326 | 327 | Assert.Equal(expected, size / divisor); 328 | } 329 | 330 | [Theory] 331 | [InlineData(0, 1.0f)] 332 | [InlineData(1, 1.0f)] 333 | [InlineData(-1, 1.0f)] 334 | [InlineData(1, -1.0f)] 335 | [InlineData(-1, -1.0f)] 336 | [InlineData(int.MaxValue, float.MaxValue)] 337 | [InlineData(int.MaxValue, float.MinValue)] 338 | [InlineData(int.MinValue, float.MaxValue)] 339 | [InlineData(int.MinValue, float.MinValue)] 340 | [InlineData(int.MaxValue, 1.0f)] 341 | [InlineData(int.MinValue, 1.0f)] 342 | [InlineData(int.MaxValue, -1.0f)] 343 | [InlineData(int.MinValue, -1.0f)] 344 | public void DivideTestSizeFloat(int dimension, float divisor) 345 | { 346 | SizeF size = new SizeF(dimension, dimension); 347 | SizeF expected; 348 | 349 | expected = new SizeF(dimension / divisor, dimension / divisor); 350 | Assert.Equal(expected, size / divisor); 351 | } 352 | 353 | [Theory] 354 | [InlineData(1111, 2222, -333.33f)] 355 | public void DivideTestSizeFloatWidthHeightDivisor(int width, int height, float divisor) 356 | { 357 | SizeF size = new SizeF(width, height); 358 | SizeF expected; 359 | 360 | expected = new SizeF(width / divisor, height / divisor); 361 | Assert.Equal(expected, size / divisor); 362 | } 363 | 364 | [Theory] 365 | [InlineData(int.MaxValue, int.MinValue)] 366 | [InlineData(int.MinValue, int.MinValue)] 367 | [InlineData(int.MaxValue, int.MaxValue)] 368 | [InlineData(0, 0)] 369 | public void DeconstructTest(int width, int height) 370 | { 371 | Size s = new Size(width, height); 372 | 373 | (int deconstructedWidth, int deconstructedHeight) = s; 374 | 375 | Assert.Equal(width, deconstructedWidth); 376 | Assert.Equal(height, deconstructedHeight); 377 | } 378 | } 379 | } -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/SixLabors.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 0.0.0 5 | netcoreapp1.1;netcoreapp2.1; 6 | SixLabors.Core.Tests 7 | SixLabors.Shapes.Tests 8 | true 9 | false 10 | false 11 | false 12 | false 13 | false 14 | false 15 | full 16 | SixLabors.Tests 17 | true 18 | 7.3 19 | 20 | 21 | 22 | ..\..\shared-infrastructure\SixLabors.Tests.ruleset 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | all 32 | runtime; build; native; contentfiles; analyzers; buildtransitive 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /tests/SixLabors.Core.Tests/TestEnvironment.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Six Labors and contributors. 2 | // Licensed under the Apache License, Version 2.0. 3 | 4 | using System; 5 | 6 | namespace SixLabors.Tests 7 | { 8 | internal class TestEnvironment 9 | { 10 | internal static bool Is64BitProcess => IntPtr.Size == 8; 11 | } 12 | } -------------------------------------------------------------------------------- /tests/SixLabors.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------