Browse Source

Merge branch 'master' into tiff-codec

pull/119/head
Andrew Wilkinson 9 years ago
parent
commit
1322e074cd
  1. 5
      ImageSharp.sln
  2. 85
      README.md
  3. 5
      appveyor.yml
  4. 58
      build/Program.cs
  5. BIN
      build/icons/imagesharp-logo-128.png
  6. BIN
      build/icons/imagesharp-logo-256.png
  7. BIN
      build/icons/imagesharp-logo-32.png
  8. BIN
      build/icons/imagesharp-logo-512.png
  9. BIN
      build/icons/imagesharp-logo-64.png
  10. BIN
      build/icons/imagesharp-logo-heading.png
  11. BIN
      build/icons/imagesharp-logo.png
  12. 2
      build/icons/imagesharp-logo.svg
  13. 40
      src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs
  14. 51
      src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs
  15. 120
      src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs
  16. 56
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  17. 53
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs
  18. 40
      src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs
  19. 6
      src/ImageSharp.Drawing/GraphicsOptions.cs
  20. 4
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  21. 8
      src/ImageSharp.Drawing/Paths/RectangleExtensions.cs
  22. 26
      src/ImageSharp.Drawing/Paths/ShapeRegion.cs
  23. 22
      src/ImageSharp.Drawing/Pens/Pen{TColor}.cs
  24. 2
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  25. 2
      src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
  26. 4
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  27. 345
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  28. 16
      src/ImageSharp.Drawing/Region.cs
  29. 9
      src/ImageSharp.Drawing/Text/DrawText.cs
  30. 23
      src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs
  31. 40
      src/ImageSharp/Colors/Color.BulkOperations.cs
  32. 8
      src/ImageSharp/Colors/ColorspaceTransforms.cs
  33. 4
      src/ImageSharp/Colors/PackedPixel/Argb.cs
  34. 28
      src/ImageSharp/Colors/PackedPixel/Bgr565.cs
  35. 84
      src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
  36. 20
      src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
  37. 36
      src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
  38. 28
      src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
  39. 28
      src/ImageSharp/Colors/PackedPixel/Rgba64.cs
  40. 16
      src/ImageSharp/Colors/PackedPixel/Short2.cs
  41. 28
      src/ImageSharp/Colors/PackedPixel/Short4.cs
  42. 8
      src/ImageSharp/Colors/Spaces/CieLab.cs
  43. 4
      src/ImageSharp/Colors/Spaces/Cmyk.cs
  44. 12
      src/ImageSharp/Colors/Spaces/Hsl.cs
  45. 12
      src/ImageSharp/Colors/Spaces/Hsv.cs
  46. 11
      src/ImageSharp/Colors/Vector4BlendTransforms.cs
  47. 4
      src/ImageSharp/Common/Extensions/Vector4Extensions.cs
  48. 22
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  49. 144
      src/ImageSharp/Common/Helpers/MathF.cs
  50. 143
      src/ImageSharp/Common/Memory/BufferPointer{T}.cs
  51. 28
      src/ImageSharp/Common/Memory/BufferSpan.cs
  52. 237
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  53. 2
      src/ImageSharp/Common/Memory/Fast2DArray{T}.cs
  54. 31
      src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs
  55. 91
      src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
  56. 45
      src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs
  57. 78
      src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs
  58. 2
      src/ImageSharp/Common/Memory/PixelDataPool{T}.cs
  59. 27
      src/ImageSharp/Configuration.cs
  60. 23
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs
  61. 5
      src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
  62. 17
      src/ImageSharp/Dithering/Ordered/Bayer.cs
  63. 9
      src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
  64. 17
      src/ImageSharp/Dithering/Ordered/Ordered.cs
  65. 38
      src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs
  66. 6
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  67. 27
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  68. 12
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  69. 50
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  70. 5
      src/ImageSharp/Formats/IImageDecoder.cs
  71. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs
  72. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs
  73. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs
  74. 7
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  75. 115
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  76. 12
      src/ImageSharp/Formats/Png/PngDecoder.cs
  77. 47
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  78. 7
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  79. 4
      src/ImageSharp/Formats/Png/PngInterlaceMode.cs
  80. 84
      src/ImageSharp/IO/BigEndianBitConverter.cs
  81. 4
      src/ImageSharp/IO/EndianBinaryReader.cs
  82. 52
      src/ImageSharp/IO/EndianBinaryWriter.cs
  83. 63
      src/ImageSharp/IO/EndianBitConverter.Conversion.cs
  84. 145
      src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs
  85. 139
      src/ImageSharp/IO/EndianBitConverter.GetBytes.cs
  86. 141
      src/ImageSharp/IO/EndianBitConverter.ToType.cs
  87. 682
      src/ImageSharp/IO/EndianBitConverter.cs
  88. 31
      src/ImageSharp/IO/IFileSystem.cs
  89. 80
      src/ImageSharp/IO/LittleEndianBitConverter.cs
  90. 32
      src/ImageSharp/IO/LocalFileSystem.cs
  91. 66
      src/ImageSharp/Image.Create.cs
  92. 75
      src/ImageSharp/Image.Decode.cs
  93. 212
      src/ImageSharp/Image.FromBytes.cs
  94. 214
      src/ImageSharp/Image.FromFile.cs
  95. 246
      src/ImageSharp/Image.FromStream.cs
  96. 199
      src/ImageSharp/Image.cs
  97. 10
      src/ImageSharp/Image/IImageBase.cs
  98. 10
      src/ImageSharp/Image/IImageBase{TColor}.cs
  99. 41
      src/ImageSharp/Image/ImageBase{TColor}.cs
  100. 354
      src/ImageSharp/Image/Image{TColor}.cs

5
ImageSharp.sln

@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
@ -22,6 +22,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"
ProjectSection(SolutionItems) = preProject
src\README.md = src\README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}"
EndProject

85
README.md

@ -9,8 +9,11 @@
[![GitHub issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues)
[![GitHub stars](https://img.shields.io/github/stars/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/network)
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Twitter](https://img.shields.io/twitter/url/https/github.com/JimBobSquarePants/ImageSharp.svg?style=social)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fJimBobSquarePants%2fImageSharp&via=james_m_south)
[![OpenCollective](https://opencollective.com/imagesharp/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/imagesharp/sponsors/badge.svg)](#sponsors)
| |Build Status|Code Coverage|
@ -74,7 +77,7 @@ Here's an example of the code required to resize an image using the default Bicu
On platforms supporting netstandard 1.3+
```csharp
using (Image image = new Image("foo.jpg"))
using (Image image = Image.Load("foo.jpg"))
{
image.Resize(image.Width / 2, image.Height / 2)
.Grayscale()
@ -85,7 +88,7 @@ on netstandard 1.1 - 1.2
```csharp
using (FileStream stream = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
using (Image image = new Image(stream))
using (Image image = Image.Load(stream))
{
image.Resize(image.Width / 2, image.Height / 2)
.Grayscale()
@ -93,12 +96,6 @@ using (Image image = new Image(stream))
}
```
Individual processors can be initialised and apply processing against images. This allows nesting which brings the potential for powerful combinations of processing methods:
```csharp
new BrightnessProcessor(50).Apply(sourceImage, sourceImage.Bounds);
```
Setting individual pixel values is perfomed as follows:
```csharp
@ -132,3 +129,73 @@ Core Team
- [Anton Firsov](https://github.com/antonfirsov)
- [Olivia Ifrim](https://github.com/olivif)
- [Scott Williams](https://github.com/tocsoft)
### Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/imagesharp#backer)]
<a href="https://opencollective.com/imagesharp/backer/0/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/1/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/2/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/3/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/4/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/5/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/6/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/7/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/8/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/9/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/10/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/11/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/12/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/13/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/14/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/15/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/16/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/17/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/18/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/19/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/20/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/21/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/22/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/23/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/24/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/25/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/26/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/27/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/28/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/backer/29/website" target="_blank"><img src="https://opencollective.com/imagesharp/backer/29/avatar.svg"></a>
### Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/imagesharp#sponsor)]
<a href="https://opencollective.com/imagesharp/sponsor/0/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/1/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/2/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/3/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/4/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/5/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/6/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/7/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/8/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/9/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/10/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/11/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/12/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/13/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/14/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/15/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/16/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/17/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/18/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/19/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/20/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/21/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/22/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/23/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/24/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/25/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/26/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/27/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/28/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/imagesharp/sponsor/29/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/29/avatar.svg"></a>

5
appveyor.yml

@ -1,6 +1,9 @@
version: 1.0.0.{build}
image: Visual Studio 2017
# prevent the double build when a branch has an active PR
skip_branch_with_pr: true
init:
- ps: iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/8cf3160c9516ef1f4effc825c0a44acc918a0b5a/appveyor-build-info.ps'))
@ -17,7 +20,7 @@ deploy:
# MyGet Deployment for builds & releases
- provider: NuGet
server: https://www.myget.org/F/imagesharp/api/v2/package
symbol_server: https://www.myget.org/F/imagesharp/api/v2/package # https://nuget.symbolsource.org/MyGet/imagesharp
symbol_server: https://www.myget.org/F/imagesharp/symbols/api/v2/package
api_key:
secure: fz0rUrt3B1HczUC1ZehwVsrFSWX9WZGDQoueDztLte9/+yQG+BBU7UrO+coE8lUf
artifact: /.*\.nupkg/

58
build/Program.cs

@ -63,21 +63,21 @@ namespace ConsoleApplication
/// <param name="args">The arguments.</param>
public static void Main(string[] args)
{
var resetmode = args.Contains("reset");
bool resetmode = args.Contains("reset");
// Find the project root
var root = Path.GetFullPath(Path.Combine(LibGit2Sharp.Repository.Discover("."), ".."));
string root = Path.GetFullPath(Path.Combine(LibGit2Sharp.Repository.Discover("."), ".."));
// Lets find the repo
var repo = new LibGit2Sharp.Repository(root);
Repository repo = new LibGit2Sharp.Repository(root);
// Lets find all the project.json files in the src folder (don't care about versioning `tests`)
var projectFiles = Directory.EnumerateFiles(Path.Combine(root, "src"), "*.csproj", SearchOption.AllDirectories);
IEnumerable<string> projectFiles = Directory.EnumerateFiles(Path.Combine(root, "src"), "*.csproj", SearchOption.AllDirectories);
ResetProject(projectFiles);
// Open them and convert them to source projects
var projects = projectFiles.Select(x => ProjectRootElement.Open(x, ProjectCollection.GlobalProjectCollection, true))
List<SourceProject> projects = projectFiles.Select(x => ProjectRootElement.Open(x, ProjectCollection.GlobalProjectCollection, true))
.Select(x => new SourceProject(x, repo.Info.WorkingDirectory))
.ToList();
@ -89,7 +89,7 @@ namespace ConsoleApplication
CreateBuildScript(projects, root);
foreach (var p in projects)
foreach (SourceProject p in projects)
{
Console.WriteLine($"{p.Name} {p.FinalVersionNumber}");
}
@ -98,10 +98,10 @@ namespace ConsoleApplication
private static void CreateBuildScript(IEnumerable<SourceProject> projects, string root)
{
var outputDir = Path.GetFullPath(Path.Combine(root, @"artifacts\bin\ImageSharp"));
string outputDir = Path.GetFullPath(Path.Combine(root, @"artifacts\bin\ImageSharp"));
var sb = new StringBuilder();
foreach (var p in projects)
StringBuilder sb = new StringBuilder();
foreach (SourceProject p in projects)
{
sb.AppendLine($@"dotnet pack --configuration Release --output ""{outputDir}"" ""{p.ProjectFilePath}""");
}
@ -111,17 +111,17 @@ namespace ConsoleApplication
private static void UpdateVersionNumbers(IEnumerable<SourceProject> projects)
{
foreach (var p in projects)
foreach (SourceProject p in projects)
{
// create a backup file so we can rollback later without breaking formatting
File.Copy(p.FullProjectFilePath, $"{p.FullProjectFilePath}.bak", true);
}
foreach (var p in projects)
foreach (SourceProject p in projects)
{
// TODO force update of all dependent projects to point to the newest build.
// we skip the build number and standard CI prefix on first commits
var newVersion = p.FinalVersionNumber;
string newVersion = p.FinalVersionNumber;
p.UpdateVersion(newVersion);
}
@ -133,13 +133,13 @@ namespace ConsoleApplication
string branch = repo.Head.FriendlyName;
// lets see if we are running in appveyor and if we are use the environment variables instead of the head
var appveryorBranch = Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH");
string appveryorBranch = Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH");
if (!string.IsNullOrWhiteSpace(appveryorBranch))
{
branch = appveryorBranch;
}
var prNumber = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER");
string prNumber = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER");
if (!string.IsNullOrWhiteSpace(prNumber))
{
branch = $"PR{int.Parse(prNumber):000}";
@ -159,7 +159,7 @@ namespace ConsoleApplication
private static void CaclulateProjectVersionNumber(List<SourceProject> projects, Repository repo)
{
var branch = CurrentBranch(repo);
string branch = CurrentBranch(repo);
// populate the dependency chains
projects.ForEach(x => x.PopulateDependencies(projects));
@ -176,7 +176,7 @@ namespace ConsoleApplication
}
// revert the project.json change be reverting it but skipp all the git stuff as its not needed
foreach (var p in projectPaths)
foreach (string p in projectPaths)
{
if (File.Exists($"{p}.bak"))
{
@ -303,7 +303,7 @@ namespace ConsoleApplication
/// <param name="branch">The branch.</param>
internal void CalculateVersion(Repository repo, string branch)
{
foreach (var c in repo.Commits)
foreach (Commit c in repo.Commits)
{
if (!this.ApplyCommit(c, repo))
{
@ -335,7 +335,7 @@ namespace ConsoleApplication
this.CommitCountSinceVersionChange++;
// return false if this is a version number root
var projectFileChange = changes.Where(x => x.Path?.Equals(this.ProjectFilePath, StringComparison.OrdinalIgnoreCase) == true).FirstOrDefault();
TreeEntryChanges projectFileChange = changes.Where(x => x.Path?.Equals(this.ProjectFilePath, StringComparison.OrdinalIgnoreCase) == true).FirstOrDefault();
if (projectFileChange != null)
{
if (projectFileChange.Status == ChangeKind.Added)
@ -345,13 +345,13 @@ namespace ConsoleApplication
}
else
{
var blob = repo.Lookup<Blob>(projectFileChange.Oid);
using (var s = blob.GetContentStream())
Blob blob = repo.Lookup<Blob>(projectFileChange.Oid);
using (Stream s = blob.GetContentStream())
{
using (var reader = XmlReader.Create(s))
using (XmlReader reader = XmlReader.Create(s))
{
var proj = ProjectRootElement.Create(reader);
var version = new NuGetVersion(proj.Properties.FirstOrDefault(x => x.Name == "VersionPrefix").Value);
ProjectRootElement proj = ProjectRootElement.Create(reader);
NuGetVersion version = new NuGetVersion(proj.Properties.FirstOrDefault(x => x.Name == "VersionPrefix").Value);
if (version != this.Version)
{
// version changed
@ -370,9 +370,9 @@ namespace ConsoleApplication
private bool ApplyCommit(Commit commit, Repository repo)
{
foreach (var parent in commit.Parents)
foreach (Commit parent in commit.Parents)
{
var changes = repo.Diff.Compare<TreeChanges>(parent.Tree, commit.Tree);
TreeChanges changes = repo.Diff.Compare<TreeChanges>(parent.Tree, commit.Tree);
foreach (TreeEntryChanges change in changes)
{
@ -399,7 +399,7 @@ namespace ConsoleApplication
private string CalculateVersionNumber(string branch)
{
var version = this.Version.ToFullString();
string version = this.Version.ToFullString();
// master only
if (this.CommitCountSinceVersionChange == 1 && branch == "master")
@ -414,12 +414,12 @@ namespace ConsoleApplication
return version;
}
var rootSpecialVersion = string.Empty;
string rootSpecialVersion = string.Empty;
if (this.Version.IsPrerelease)
{
// probably a much easy way for doing this but it work sell enough for a build script
var parts = version.Split(new[] { '-' }, 2);
string[] parts = version.Split(new[] { '-' }, 2);
version = parts[0];
rootSpecialVersion = parts[1];
}
@ -447,7 +447,7 @@ namespace ConsoleApplication
branch = "-" + branch;
}
var maxLength = 20; // dotnet will fail to populate the package if the tag is > 20
int maxLength = 20; // dotnet will fail to populate the package if the tag is > 20
maxLength -= rootSpecialVersion.Length; // this is a required tag
maxLength -= 7; // for the counter and dashes

BIN
build/icons/imagesharp-logo-128.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
build/icons/imagesharp-logo-256.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
build/icons/imagesharp-logo-32.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
build/icons/imagesharp-logo-512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
build/icons/imagesharp-logo-64.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
build/icons/imagesharp-logo-heading.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
build/icons/imagesharp-logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 34 KiB

2
build/icons/imagesharp-logo.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

40
src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs

@ -18,10 +18,9 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// Percent10 Hatch Pattern
/// </summary>
/// note 2d arrays when configured using initalizer look inverted
/// ---> Y axis
/// ---> x axis
/// ^
/// | X - axis
/// | y - axis
/// |
/// see PatternBrush for details about how to make new patterns work
private static readonly bool[,] Percent10Pattern =
@ -37,10 +36,10 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private static readonly bool[,] Percent20Pattern =
{
{ true, false, true, false },
{ false, false, false, false },
{ false, true, false, true },
{ false, false, false, false }
{ true, false, false, false },
{ false, false, true, false },
{ true, false, false, false },
{ false, false, true, false }
};
/// <summary>
@ -48,7 +47,10 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private static readonly bool[,] HorizontalPattern =
{
{ false, true, false, false },
{ false },
{ true },
{ false },
{ false }
};
/// <summary>
@ -56,7 +58,10 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private static readonly bool[,] MinPattern =
{
{ false, false, false, true },
{ false },
{ false },
{ false },
{ true }
};
/// <summary>
@ -64,10 +69,7 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private static readonly bool[,] VerticalPattern =
{
{ false },
{ true },
{ false },
{ false }
{ false, true, false, false },
};
/// <summary>
@ -75,10 +77,10 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private static readonly bool[,] ForwardDiagonalPattern =
{
{ true, false, false, false },
{ false, true, false, false },
{ false, false, false, true },
{ false, false, true, false },
{ false, false, false, true }
{ false, true, false, false },
{ true, false, false, false }
};
/// <summary>
@ -86,10 +88,10 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private static readonly bool[,] BackwardDiagonalPattern =
{
{ false, false, false, true },
{ false, false, true, false },
{ true, false, false, false },
{ false, true, false, false },
{ true, false, false, false }
{ false, false, true, false },
{ false, false, false, true }
};
/// <summary>

51
src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs

@ -34,7 +34,7 @@ namespace ImageSharp.Drawing.Brushes
/// <inheritdoc />
public BrushApplicator<TColor> CreateApplicator(PixelAccessor<TColor> sourcePixels, RectangleF region)
{
return new ImageBrushApplicator(this.image, region);
return new ImageBrushApplicator(sourcePixels, this.image, region);
}
/// <summary>
@ -71,12 +71,16 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="region">
/// The region.
/// </param>
public ImageBrushApplicator(IImageBase<TColor> image, RectangleF region)
/// <param name="sourcePixels">
/// The sourcePixels.
/// </param>
public ImageBrushApplicator(PixelAccessor<TColor> sourcePixels, IImageBase<TColor> image, RectangleF region)
: base(sourcePixels)
{
this.source = image.Lock();
this.xLength = image.Width;
this.yLength = image.Height;
this.offset = new Vector2((float)Math.Max(Math.Floor(region.Top), 0), (float)Math.Max(Math.Floor(region.Left), 0));
this.offset = new Vector2(MathF.Max(MathF.Floor(region.Top), 0), MathF.Max(MathF.Floor(region.Left), 0));
}
/// <summary>
@ -87,18 +91,18 @@ namespace ImageSharp.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
public override TColor this[int x, int y]
internal override TColor this[int x, int y]
{
get
{
var point = new Vector2(x, y);
Vector2 point = new Vector2(x, y);
// Offset the requested pixel by the value in the rectangle (the shapes position)
point = point - this.offset;
x = (int)point.X % this.xLength;
y = (int)point.Y % this.yLength;
int srcX = (int)point.X % this.xLength;
int srcY = (int)point.Y % this.yLength;
return this.source[x, y];
return this.source[srcX, srcY];
}
}
@ -107,6 +111,37 @@ namespace ImageSharp.Drawing.Brushes
{
this.source.Dispose();
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = this[targetX, targetY].ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
}
}
}
}
}
}

120
src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs

@ -30,15 +30,6 @@ namespace ImageSharp.Drawing.Brushes
/// 0
/// 0
/// </para>
/// Warning when use array initializer across multiple lines the bools look inverted i.e.
/// new bool[,]{
/// {true, false, false},
/// {false,true, false}
/// }
/// would be
/// 10
/// 01
/// 00
/// </remarks>
/// <typeparam name="TColor">The pixel format.</typeparam>
public class PatternBrush<TColor> : IBrush<TColor>
@ -47,12 +38,19 @@ namespace ImageSharp.Drawing.Brushes
/// <summary>
/// The pattern.
/// </summary>
private readonly TColor[][] pattern;
private readonly Fast2DArray<TColor> pattern;
private readonly Fast2DArray<Vector4> patternVector;
/// <summary>
/// The stride width.
/// Initializes a new instance of the <see cref="PatternBrush{TColor}"/> class.
/// </summary>
private readonly int stride;
/// <param name="foreColor">Color of the fore.</param>
/// <param name="backColor">Color of the back.</param>
/// <param name="pattern">The pattern.</param>
public PatternBrush(TColor foreColor, TColor backColor, bool[,] pattern)
: this(foreColor, backColor, new Fast2DArray<bool>(pattern))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrush{TColor}"/> class.
@ -60,26 +58,23 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="foreColor">Color of the fore.</param>
/// <param name="backColor">Color of the back.</param>
/// <param name="pattern">The pattern.</param>
public PatternBrush(TColor foreColor, TColor backColor, bool[,] pattern)
internal PatternBrush(TColor foreColor, TColor backColor, Fast2DArray<bool> pattern)
{
this.stride = pattern.GetLength(1);
// Convert the multidimension array into a jagged one.
int height = pattern.GetLength(0);
this.pattern = new TColor[height][];
for (int x = 0; x < height; x++)
Vector4 foreColorVector = foreColor.ToVector4();
Vector4 backColorVector = backColor.ToVector4();
this.pattern = new Fast2DArray<TColor>(pattern.Width, pattern.Height);
this.patternVector = new Fast2DArray<Vector4>(pattern.Width, pattern.Height);
for (int i = 0; i < pattern.Data.Length; i++)
{
this.pattern[x] = new TColor[this.stride];
for (int y = 0; y < this.stride; y++)
if (pattern.Data[i])
{
if (pattern[x, y])
{
this.pattern[x][y] = foreColor;
}
else
{
this.pattern[x][y] = backColor;
}
this.pattern.Data[i] = foreColor;
this.patternVector.Data[i] = foreColorVector;
}
else
{
this.pattern.Data[i] = backColor;
this.patternVector.Data[i] = backColorVector;
}
}
}
@ -91,13 +86,13 @@ namespace ImageSharp.Drawing.Brushes
internal PatternBrush(PatternBrush<TColor> brush)
{
this.pattern = brush.pattern;
this.stride = brush.stride;
this.patternVector = brush.patternVector;
}
/// <inheritdoc />
public BrushApplicator<TColor> CreateApplicator(PixelAccessor<TColor> sourcePixels, RectangleF region)
{
return new PatternBrushApplicator(this.pattern, this.stride);
return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector);
}
/// <summary>
@ -105,31 +100,23 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private class PatternBrushApplicator : BrushApplicator<TColor>
{
/// <summary>
/// The patter x-length.
/// </summary>
private readonly int xLength;
/// <summary>
/// The stride width.
/// </summary>
private readonly int stride;
/// <summary>
/// The pattern.
/// </summary>
private readonly TColor[][] pattern;
private readonly Fast2DArray<TColor> pattern;
private readonly Fast2DArray<Vector4> patternVector;
/// <summary>
/// Initializes a new instance of the <see cref="PatternBrushApplicator" /> class.
/// </summary>
/// <param name="sourcePixels">The sourcePixels.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="stride">The stride.</param>
public PatternBrushApplicator(TColor[][] pattern, int stride)
/// <param name="patternVector">The patternVector.</param>
public PatternBrushApplicator(PixelAccessor<TColor> sourcePixels, Fast2DArray<TColor> pattern, Fast2DArray<Vector4> patternVector)
: base(sourcePixels)
{
this.pattern = pattern;
this.xLength = pattern.Length;
this.stride = stride;
this.patternVector = patternVector;
}
/// <summary>
@ -140,14 +127,15 @@ namespace ImageSharp.Drawing.Brushes
/// <returns>
/// The Color.
/// </returns>
public override TColor this[int x, int y]
internal override TColor this[int x, int y]
{
get
{
x = x % this.xLength;
y = y % this.stride;
x = x % this.pattern.Width;
y = y % this.pattern.Height;
return this.pattern[x][y];
// 2d array index at row/column
return this.pattern[y, x];
}
}
@ -156,6 +144,38 @@ namespace ImageSharp.Drawing.Brushes
{
// noop
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
// 2d array index at row/column
Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width];
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
}
}
}
}
}
}

56
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -7,6 +7,7 @@ namespace ImageSharp.Drawing.Processors
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// primitive that converts a point in to a color for discovering the fill color based on an implementation
@ -16,15 +17,68 @@ namespace ImageSharp.Drawing.Processors
public abstract class BrushApplicator<TColor> : IDisposable // disposable will be required if/when there is an ImageBrush
where TColor : struct, IPixel<TColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="BrushApplicator{TColor}"/> class.
/// </summary>
/// <param name="target">The target.</param>
internal BrushApplicator(PixelAccessor<TColor> target)
{
this.Target = target;
}
/// <summary>
/// Gets the destinaion
/// </summary>
protected PixelAccessor<TColor> Target { get; }
/// <summary>
/// Gets the color for a single pixel.
/// </summary>
/// <param name="x">The x cordinate.</param>
/// <param name="y">The y cordinate.</param>
/// <returns>The a <typeparamref name="TColor"/> that should be applied to the pixel.</returns>
public abstract TColor this[int x, int y] { get; }
internal abstract TColor this[int x, int y] { get; }
/// <inheritdoc/>
public abstract void Dispose();
/// <summary>
/// Applies the opactiy weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
/// </summary>
/// <param name="scanlineBuffer">The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.</param>
/// <param name="scanlineWidth">The number of pixels effected by this scanline.</param>
/// <param name="offset">The offset fromthe begining of <paramref name="scanlineBuffer" /> the opacity data starts.</param>
/// <param name="x">The x position in the target pixel space that the start of the scanline data corresponds to.</param>
/// <param name="y">The y position in the target pixel space that whole scanline corresponds to.</param>
/// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
internal virtual void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
{
DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = this[targetX, targetY].ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
}
}
}
}
}

53
src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs

@ -65,11 +65,6 @@ namespace ImageSharp.Drawing.Brushes
/// </summary>
private class RecolorBrushApplicator : BrushApplicator<TColor>
{
/// <summary>
/// The source pixel accessor.
/// </summary>
private readonly PixelAccessor<TColor> source;
/// <summary>
/// The source color.
/// </summary>
@ -93,8 +88,8 @@ namespace ImageSharp.Drawing.Brushes
/// <param name="targetColor">Color of the target.</param>
/// <param name="threshold">The threshold .</param>
public RecolorBrushApplicator(PixelAccessor<TColor> sourcePixels, TColor sourceColor, TColor targetColor, float threshold)
: base(sourcePixels)
{
this.source = sourcePixels;
this.sourceColor = sourceColor.ToVector4();
this.targetColor = targetColor.ToVector4();
@ -114,17 +109,17 @@ namespace ImageSharp.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
public override TColor this[int x, int y]
internal override TColor this[int x, int y]
{
get
{
// Offset the requested pixel by the value in the rectangle (the shapes position)
TColor result = this.source[x, y];
TColor result = this.Target[x, y];
Vector4 background = result.ToVector4();
float distance = Vector4.DistanceSquared(background, this.sourceColor);
if (distance <= this.threshold)
{
var lerpAmount = (this.threshold - distance) / this.threshold;
float lerpAmount = (this.threshold - distance) / this.threshold;
Vector4 blended = Vector4BlendTransforms.PremultipliedLerp(
background,
this.targetColor,
@ -140,6 +135,46 @@ namespace ImageSharp.Drawing.Brushes
public override void Dispose()
{
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = backgroundVector;
float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor);
if (distance <= this.threshold)
{
float lerpAmount = (this.threshold - distance) / this.threshold;
sourceVector = Vector4BlendTransforms.PremultipliedLerp(
sourceVector,
this.targetColor,
lerpAmount);
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
}
}
}
}
}
}
}

40
src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs

@ -42,7 +42,7 @@ namespace ImageSharp.Drawing.Brushes
/// <inheritdoc />
public BrushApplicator<TColor> CreateApplicator(PixelAccessor<TColor> sourcePixels, RectangleF region)
{
return new SolidBrushApplicator(this.color);
return new SolidBrushApplicator(sourcePixels, this.color);
}
/// <summary>
@ -54,14 +54,18 @@ namespace ImageSharp.Drawing.Brushes
/// The solid color.
/// </summary>
private readonly TColor color;
private readonly Vector4 colorVector;
/// <summary>
/// Initializes a new instance of the <see cref="SolidBrushApplicator"/> class.
/// </summary>
/// <param name="color">The color.</param>
public SolidBrushApplicator(TColor color)
/// <param name="sourcePixels">The sourcePixels.</param>
public SolidBrushApplicator(PixelAccessor<TColor> sourcePixels, TColor color)
: base(sourcePixels)
{
this.color = color;
this.colorVector = color.ToVector4();
}
/// <summary>
@ -72,13 +76,43 @@ namespace ImageSharp.Drawing.Brushes
/// <returns>
/// The color
/// </returns>
public override TColor this[int x, int y] => this.color;
internal override TColor this[int x, int y] => this.color;
/// <inheritdoc />
public override void Dispose()
{
// noop
}
/// <inheritdoc />
internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y)
{
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer))
{
BufferSpan<float> slice = buffer.Slice(offset);
for (int xPos = 0; xPos < scanlineWidth; xPos++)
{
int targetX = xPos + x;
int targetY = y;
float opacity = slice[xPos];
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4();
Vector4 sourceVector = this.colorVector;
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
this.Target[targetX, targetY] = packed;
}
}
}
}
}
}
}

6
src/ImageSharp.Drawing/GraphicsOptions.cs

@ -20,6 +20,11 @@ namespace ImageSharp.Drawing
/// </summary>
public bool Antialias;
/// <summary>
/// The number of subpixels to use while rendering with antialiasing enabled.
/// </summary>
public int AntialiasSubpixelDepth;
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
@ -27,6 +32,7 @@ namespace ImageSharp.Drawing
public GraphicsOptions(bool enableAntialiasing)
{
this.Antialias = enableAntialiasing;
this.AntialiasSubpixelDepth = 16;
}
}
}

4
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<AssemblyTitle>ImageSharp.Drawing</AssemblyTitle>
<VersionPrefix>1.0.0-alpha3</VersionPrefix>
<VersionPrefix>1.0.0-alpha5</VersionPrefix>
<Authors>James Jackson-South and contributors</Authors>
<TargetFramework>netstandard1.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -40,7 +40,7 @@
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="SixLabors.Fonts" Version="0.1.0-alpha0002" />
<PackageReference Include="SixLabors.Shapes" Version="0.1.0-alpha0008" />
<PackageReference Include="SixLabors.Shapes" Version="0.1.0-alpha0009" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>

8
src/ImageSharp.Drawing/Paths/RectangleExtensions.cs

@ -19,10 +19,10 @@ namespace ImageSharp.Drawing
/// <returns>A <see cref="Rectangle"/> representation of this <see cref="SixLabors.Shapes.Rectangle"/></returns>
public static Rectangle Convert(this SixLabors.Shapes.Rectangle source)
{
int left = (int)Math.Floor(source.Left);
int right = (int)Math.Ceiling(source.Right);
int top = (int)Math.Floor(source.Top);
int bottom = (int)Math.Ceiling(source.Bottom);
int left = (int)MathF.Floor(source.Left);
int right = (int)MathF.Ceiling(source.Right);
int top = (int)MathF.Floor(source.Top);
int bottom = (int)MathF.Ceiling(source.Bottom);
return new Rectangle(left, top, right - left, bottom - top);
}
}

26
src/ImageSharp.Drawing/Paths/ShapeRegion.cs

@ -5,6 +5,7 @@
namespace ImageSharp.Drawing
{
using System;
using System.Buffers;
using System.Numerics;
@ -39,30 +40,7 @@ namespace ImageSharp.Drawing
public override Rectangle Bounds { get; }
/// <inheritdoc/>
public override int ScanX(int x, float[] buffer, int length, int offset)
{
Vector2 start = new Vector2(x, this.Bounds.Top - 1);
Vector2 end = new Vector2(x, this.Bounds.Bottom + 1);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(length);
try
{
int count = this.Shape.FindIntersections(start, end, innerbuffer, length, 0);
for (int i = 0; i < count; i++)
{
buffer[i + offset] = innerbuffer[i].Y;
}
return count;
}
finally
{
ArrayPool<Vector2>.Shared.Return(innerbuffer);
}
}
/// <inheritdoc/>
public override int ScanY(int y, float[] buffer, int length, int offset)
public override int Scan(float y, float[] buffer, int length, int offset)
{
Vector2 start = new Vector2(this.Bounds.Left - 1, y);
Vector2 end = new Vector2(this.Bounds.Right + 1, y);

22
src/ImageSharp.Drawing/Pens/Pen{TColor}.cs

@ -146,7 +146,7 @@ namespace ImageSharp.Drawing.Pens
public override ColoredPointInfo<TColor> GetColor(int x, int y, PointInfo info)
{
var result = default(ColoredPointInfo<TColor>);
ColoredPointInfo<TColor> result = default(ColoredPointInfo<TColor>);
result.Color = this.brush[x, y];
if (info.DistanceFromPath < this.halfWidth)
@ -178,7 +178,7 @@ namespace ImageSharp.Drawing.Pens
this.pattern = new float[pattern.Length + 1];
this.pattern[0] = 0;
for (var i = 0; i < pattern.Length; i++)
for (int i = 0; i < pattern.Length; i++)
{
this.totalLength += pattern[i] * width;
this.pattern[i + 1] = this.totalLength;
@ -199,10 +199,10 @@ namespace ImageSharp.Drawing.Pens
public override ColoredPointInfo<TColor> GetColor(int x, int y, PointInfo info)
{
var infoResult = default(ColoredPointInfo<TColor>);
ColoredPointInfo<TColor> infoResult = default(ColoredPointInfo<TColor>);
infoResult.DistanceFromElement = float.MaxValue; // is really outside the element
var length = info.DistanceAlongPath % this.totalLength;
float length = info.DistanceAlongPath % this.totalLength;
// we can treat the DistanceAlongPath and DistanceFromPath as x,y coords for the pattern
// we need to calcualte the distance from the outside edge of the pattern
@ -221,10 +221,10 @@ namespace ImageSharp.Drawing.Pens
distanceWAway = info.DistanceFromPath - this.halfWidth;
}
for (var i = 0; i < this.pattern.Length - 1; i++)
for (int i = 0; i < this.pattern.Length - 1; i++)
{
var start = this.pattern[i];
var end = this.pattern[i + 1];
float start = this.pattern[i];
float end = this.pattern[i + 1];
if (length >= start && length < end)
{
@ -238,12 +238,12 @@ namespace ImageSharp.Drawing.Pens
else
{
// this is a none solid part
var distanceFromStart = length - start;
var distanceFromEnd = end - length;
float distanceFromStart = length - start;
float distanceFromEnd = end - length;
var closestEdge = Math.Min(distanceFromStart, distanceFromEnd);
float closestEdge = MathF.Min(distanceFromStart, distanceFromEnd);
var distanceAcross = closestEdge;
float distanceAcross = closestEdge;
if (distanceWAway > 0)
{

2
src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs

@ -15,7 +15,7 @@ namespace ImageSharp.Drawing.Processors
/// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public class DrawImageProcessor<TColor> : ImageProcessor<TColor>
internal class DrawImageProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>

2
src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs

@ -17,7 +17,7 @@ namespace ImageSharp.Drawing.Processors
/// </summary>
/// <typeparam name="TColor">The type of the color.</typeparam>
/// <seealso cref="ImageSharp.Processing.ImageProcessor{TColor}" />
public class DrawPathProcessor<TColor> : ImageProcessor<TColor>
internal class DrawPathProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPixel<TColor>
{
private const float AntialiasFactor = 1f;

4
src/ImageSharp.Drawing/Processors/FillProcessor.cs

@ -16,8 +16,8 @@ namespace ImageSharp.Drawing.Processors
/// Using the bursh as a source of pixels colors blends the brush color with source.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public class FillProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPixel<TColor>
internal class FillProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>
/// The brush.

345
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -17,7 +17,7 @@ namespace ImageSharp.Drawing.Processors
/// </summary>
/// <typeparam name="TColor">The type of the color.</typeparam>
/// <seealso cref="ImageSharp.Processing.ImageProcessor{TColor}" />
public class FillRegionProcessor<TColor> : ImageProcessor<TColor>
internal class FillRegionProcessor<TColor> : ImageProcessor<TColor>
where TColor : struct, IPixel<TColor>
{
private const float AntialiasFactor = 1f;
@ -57,315 +57,136 @@ namespace ImageSharp.Drawing.Processors
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
Rectangle rect = this.Region.Bounds;
int polyStartY = sourceRectangle.Y - DrawPadding;
int polyEndY = sourceRectangle.Bottom + DrawPadding;
int startX = sourceRectangle.X - DrawPadding;
int endX = sourceRectangle.Right + DrawPadding;
int minX = Math.Max(sourceRectangle.Left, startX);
int maxX = Math.Min(sourceRectangle.Right - 1, endX);
int minY = Math.Max(sourceRectangle.Top, polyStartY);
int maxY = Math.Min(sourceRectangle.Bottom - 1, polyEndY);
Region region = this.Region;
Rectangle rect = region.Bounds;
// Align start/end positions.
minX = Math.Max(0, minX);
maxX = Math.Min(source.Width, maxX);
minY = Math.Max(0, minY);
maxY = Math.Min(source.Height, maxY);
int minX = Math.Max(0, rect.Left);
int maxX = Math.Min(source.Width, rect.Right);
int minY = Math.Max(0, rect.Top);
int maxY = Math.Min(source.Height, rect.Bottom);
if (minX >= maxX)
{
return; // no effect inside image;
}
if (minY >= maxY)
{
return; // no effect inside image;
}
ArrayPool<float> arrayPool = ArrayPool<float>.Shared;
int maxIntersections = this.Region.MaxIntersections;
int maxIntersections = region.MaxIntersections;
float subpixelCount = 4;
if (this.Options.Antialias)
{
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
subpixelCount = 4;
}
}
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (BrushApplicator<TColor> applicator = this.Brush.CreateApplicator(sourcePixels, rect))
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
(int y) =>
float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX;
float[] scanline = ArrayPool<float>.Shared.Rent(scanlineWidth);
try
{
float[] buffer = arrayPool.Rent(maxIntersections);
try
bool scanlineDirty = true;
for (int y = minY; y < maxY; y++)
{
float right = endX;
// foreach line we get all the points where this line crosses the polygon
int pointsFound = this.Region.ScanY(y, buffer, maxIntersections, 0);
if (pointsFound == 0)
if (scanlineDirty)
{
// nothing on this line skip
return;
}
QuickSort(buffer, pointsFound);
int currentIntersection = 0;
float nextPoint = buffer[0];
float lastPoint = float.MinValue;
bool isInside = false;
for (int x = minX; x < maxX; x++)
{
if (!isInside)
{
if (x < (nextPoint - DrawPadding) && x > (lastPoint + DrawPadding))
{
if (nextPoint == right)
{
// we are in the ends run skip it
x = maxX;
continue;
}
// lets just jump forward
x = (int)Math.Floor(nextPoint) - DrawPadding;
}
}
bool onCorner = false;
// there seems to be some issue with this switch.
if (x >= nextPoint)
// clear the buffer
for (int x = 0; x < scanlineWidth; x++)
{
currentIntersection++;
lastPoint = nextPoint;
if (currentIntersection == pointsFound)
{
nextPoint = right;
}
else
{
nextPoint = buffer[currentIntersection];
// double point from a corner flip the bit back and move on again
if (nextPoint == lastPoint)
{
onCorner = true;
isInside ^= true;
currentIntersection++;
if (currentIntersection == pointsFound)
{
nextPoint = right;
}
else
{
nextPoint = buffer[currentIntersection];
}
}
}
isInside ^= true;
scanline[x] = 0;
}
float opacity = 1;
if (!isInside && !onCorner)
{
if (this.Options.Antialias)
{
float distance = float.MaxValue;
if (x == lastPoint || x == nextPoint)
{
// we are to far away from the line
distance = 0;
}
else if (nextPoint - AntialiasFactor < x)
{
// we are near the left of the line
distance = nextPoint - x;
}
else if (lastPoint + AntialiasFactor > x)
{
// we are near the right of the line
distance = x - lastPoint;
}
else
{
// we are to far away from the line
continue;
}
opacity = 1 - (distance / AntialiasFactor);
}
else
{
continue;
}
}
if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
Vector4 sourceVector = applicator[x, y].ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
sourcePixels[x, y] = packed;
}
scanlineDirty = false;
}
}
finally
{
arrayPool.Return(buffer);
}
});
if (this.Options.Antialias)
{
// we only need to do the X can for antialiasing purposes
Parallel.For(
minX,
maxX,
this.ParallelOptions,
(int x) =>
{
float[] buffer = arrayPool.Rent(maxIntersections);
try
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{
float left = polyStartY;
float right = polyEndY;
// foreach line we get all the points where this line crosses the polygon
int pointsFound = this.Region.ScanX(x, buffer, maxIntersections, 0);
int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0);
if (pointsFound == 0)
{
// nothign on this line skip
return;
// nothing on this line skip
continue;
}
QuickSort(buffer, pointsFound);
int currentIntersection = 0;
float nextPoint = buffer[0];
float lastPoint = left;
bool isInside = false;
for (int y = minY; y < maxY; y++)
for (int point = 0; point < pointsFound; point += 2)
{
if (!isInside)
// points will be paired up
float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart);
int endX = (int)MathF.Floor(scanEnd);
if (startX >= 0 && startX < scanline.Length)
{
if (y < (nextPoint - DrawPadding) && y > (lastPoint + DrawPadding))
for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
if (nextPoint == right)
{
// we are in the ends run skip it
y = maxY;
continue;
}
// lets just jump forward
y = (int)Math.Floor(nextPoint) - DrawPadding;
scanline[startX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
else
if (endX >= 0 && endX < scanline.Length)
{
if (y < nextPoint - DrawPadding)
for (float x = endX; x < scanEnd; x += subpixelFraction)
{
if (nextPoint == right)
{
// we are in the ends run skip it
y = maxY;
continue;
}
// lets just jump forward
y = (int)Math.Floor(nextPoint);
scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
bool onCorner = false;
if (y >= nextPoint)
int nextX = startX + 1;
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
if (nextX >= 0)
{
currentIntersection++;
lastPoint = nextPoint;
if (currentIntersection == pointsFound)
{
nextPoint = right;
}
else
for (int x = nextX; x < endX; x++)
{
nextPoint = buffer[currentIntersection];
// double point from a corner flip the bit back and move on again
if (nextPoint == lastPoint)
{
onCorner = true;
isInside ^= true;
currentIntersection++;
if (currentIntersection == pointsFound)
{
nextPoint = right;
}
else
{
nextPoint = buffer[currentIntersection];
}
}
scanline[x] += subpixelFraction;
scanlineDirty = true;
}
isInside ^= true;
}
}
}
float opacity = 1;
if (!isInside && !onCorner)
if (scanlineDirty)
{
if (!this.Options.Antialias)
{
for (int x = 0; x < scanlineWidth; x++)
{
if (this.Options.Antialias)
if (scanline[x] > 0.5)
{
float distance = float.MaxValue;
if (y == lastPoint || y == nextPoint)
{
// we are to far away from the line
distance = 0;
}
else if (nextPoint - AntialiasFactor < y)
{
// we are near the left of the line
distance = nextPoint - y;
}
else if (lastPoint + AntialiasFactor > y)
{
// we are near the right of the line
distance = y - lastPoint;
}
else
{
// we are to far away from the line
continue;
}
opacity = 1 - (distance / AntialiasFactor);
scanline[x] = 1;
}
else
{
continue;
scanline[x] = 0;
}
}
// don't set full opactiy color as it will have been gotten by the first scan
if (opacity > Constants.Epsilon && opacity < 1)
{
Vector4 backgroundVector = sourcePixels[x, y].ToVector4();
Vector4 sourceVector = applicator[x, y].ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
finalColor.W = backgroundVector.W;
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
sourcePixels[x, y] = packed;
}
}
applicator.Apply(scanline, scanlineWidth, 0, minX, y);
}
finally
{
arrayPool.Return(buffer);
}
});
}
}
finally
{
arrayPool.Return(buffer);
ArrayPool<float>.Shared.Return(scanline);
}
}
}

16
src/ImageSharp.Drawing/Region.cs

@ -19,28 +19,18 @@ namespace ImageSharp.Drawing
/// Gets the bounding box that entirely surrounds this region.
/// </summary>
/// <remarks>
/// This should always contains all possible points returned from either <see cref="ScanX(int, float[], int, int)"/> or <see cref="ScanY(int, float[], int, int)"/>.
/// This should always contains all possible points returned from <see cref="Scan(float, float[], int, int)"/>.
/// </remarks>
public abstract Rectangle Bounds { get; }
/// <summary>
/// Scans the X axis for intersections.
/// </summary>
/// <param name="x">The position along the X axis to find intersections.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="length">The length.</param>
/// <param name="offset">The offset.</param>
/// <returns>The number of intersections found.</returns>
public abstract int ScanX(int x, float[] buffer, int length, int offset);
/// <summary>
/// Scans the Y axis for intersections.
/// Scans the X axis for intersections at the Y axis position.
/// </summary>
/// <param name="y">The position along the y axis to find intersections.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="length">The length.</param>
/// <param name="offset">The offset.</param>
/// <returns>The number of intersections found.</returns>
public abstract int ScanY(int y, float[] buffer, int length, int offset);
public abstract int Scan(float y, float[] buffer, int length, int offset);
}
}

9
src/ImageSharp.Drawing/Text/DrawText.cs

@ -18,6 +18,8 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
private static readonly Vector2 DefaultTextDpi = new Vector2(72);
/// <summary>
/// Draws the text onto the the image filled via the brush.
/// </summary>
@ -169,7 +171,12 @@ namespace ImageSharp
TextRenderer renderer = new TextRenderer(glyphBuilder);
Vector2 dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
Vector2 dpi = DefaultTextDpi;
if (options.UseImageResolution)
{
dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
}
FontSpan style = new FontSpan(font)
{
ApplyKerning = options.ApplyKerning,

23
src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs

@ -20,6 +20,11 @@ namespace ImageSharp.Drawing
/// </summary>
public bool Antialias;
/// <summary>
/// The number of subpixels to use while rendering with antialiasing enabled.
/// </summary>
public int AntialiasSubpixelDepth;
/// <summary>
/// Whether the text should be drawing with kerning enabled.
/// </summary>
@ -30,6 +35,12 @@ namespace ImageSharp.Drawing
/// </summary>
public float TabWidth;
/// <summary>
/// Flag weather to use the current image resultion to for point size scaling.
/// If this is [false] the text renders at 72dpi otherwise it renders at Image resolution
/// </summary>
public bool UseImageResolution;
/// <summary>
/// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct.
/// </summary>
@ -39,6 +50,8 @@ namespace ImageSharp.Drawing
this.Antialias = enableAntialiasing;
this.ApplyKerning = true;
this.TabWidth = 4;
this.AntialiasSubpixelDepth = 16;
this.UseImageResolution = false;
}
/// <summary>
@ -50,7 +63,10 @@ namespace ImageSharp.Drawing
/// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{
return new TextGraphicsOptions(options.Antialias);
return new TextGraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth
};
}
/// <summary>
@ -62,7 +78,10 @@ namespace ImageSharp.Drawing
/// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{
return new GraphicsOptions(options.Antialias);
return new GraphicsOptions(options.Antialias)
{
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth
};
}
}
}

40
src/ImageSharp/Colors/Color.BulkOperations.cs

@ -24,8 +24,8 @@ namespace ImageSharp
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>
/// that works only with `count` divisible by <see cref="Vector{UInt32}.Count"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the dstination vectors.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the dstination vectors.</param>
/// <param name="count">The number of pixels to convert.</param>
/// <remarks>
/// Implementation adapted from:
@ -38,10 +38,16 @@ namespace ImageSharp
/// </see>
/// </remarks>
internal static unsafe void ToVector4SimdAligned(
BufferPointer<Color> sourceColors,
BufferPointer<Vector4> destVectors,
BufferSpan<Color> sourceColors,
BufferSpan<Vector4> destVectors,
int count)
{
if (!Vector.IsHardwareAccelerated)
{
throw new InvalidOperationException(
"Color.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!");
}
int vecSize = Vector<uint>.Count;
DebugGuard.IsTrue(
@ -85,14 +91,14 @@ namespace ImageSharp
vf.CopyTo(fTemp, i);
}
BufferPointer.Copy<uint>(tempBuf, (BufferPointer<byte>)destVectors, unpackedRawCount);
BufferSpan.Copy<uint>(tempBuf, (BufferSpan<byte>)destVectors, unpackedRawCount);
}
}
/// <inheritdoc />
internal override void ToVector4(BufferPointer<Color> sourceColors, BufferPointer<Vector4> destVectors, int count)
internal override void ToVector4(BufferSpan<Color> sourceColors, BufferSpan<Vector4> destVectors, int count)
{
if (count < 256)
if (count < 256 || !Vector.IsHardwareAccelerated)
{
// Doesn't worth to bother with SIMD:
base.ToVector4(sourceColors, destVectors, count);
@ -117,7 +123,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void PackFromXyzBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override unsafe void PackFromXyzBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
@ -132,7 +138,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void ToXyzBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override unsafe void ToXyzBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
@ -149,19 +155,19 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override void PackFromXyzwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override void PackFromXyzwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
BufferPointer.Copy(sourceBytes, destColors, count);
BufferSpan.Copy(sourceBytes, destColors, count);
}
/// <inheritdoc />
internal override void ToXyzwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override void ToXyzwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
BufferPointer.Copy(sourceColors, destBytes, count);
BufferSpan.Copy(sourceColors, destBytes, count);
}
/// <inheritdoc />
internal override unsafe void PackFromZyxBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override unsafe void PackFromZyxBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
@ -176,7 +182,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void ToZyxBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override unsafe void ToZyxBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
@ -193,7 +199,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void PackFromZyxwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override unsafe void PackFromZyxwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
@ -208,7 +214,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void ToZyxwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override unsafe void ToZyxwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;

8
src/ImageSharp/Colors/ColorspaceTransforms.cs

@ -105,12 +105,12 @@ namespace ImageSharp
float s = color.S;
float v = color.V;
if (Math.Abs(s) < Constants.Epsilon)
if (MathF.Abs(s) < Constants.Epsilon)
{
return new Color(v, v, v, 1);
}
float h = (Math.Abs(color.H - 360) < Constants.Epsilon) ? 0 : color.H / 60;
float h = (MathF.Abs(color.H - 360) < Constants.Epsilon) ? 0 : color.H / 60;
int i = (int)Math.Truncate(h);
float f = h - i;
@ -178,9 +178,9 @@ namespace ImageSharp
float s = color.S;
float l = color.L;
if (Math.Abs(l) > Constants.Epsilon)
if (MathF.Abs(l) > Constants.Epsilon)
{
if (Math.Abs(s) < Constants.Epsilon)
if (MathF.Abs(s) < Constants.Epsilon)
{
r = g = b = l;
}

4
src/ImageSharp/Colors/PackedPixel/Argb.cs

@ -307,7 +307,7 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(float x, float y, float z, float w)
{
var value = new Vector4(x, y, z, w);
Vector4 value = new Vector4(x, y, z, w);
return Pack(ref value);
}
@ -333,7 +333,7 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(ref Vector3 vector)
{
var value = new Vector4(vector, 1);
Vector4 value = new Vector4(vector, 1);
return Pack(ref value);
}

28
src/ImageSharp/Colors/PackedPixel/Bgr565.cs

@ -110,9 +110,9 @@ namespace ImageSharp
public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
@ -120,10 +120,10 @@ namespace ImageSharp
public void ToXyzwBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -131,9 +131,9 @@ namespace ImageSharp
public void ToZyxBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -141,10 +141,10 @@ namespace ImageSharp
public void ToZyxwBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

84
src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs

@ -29,12 +29,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromVector4(Vector4)"/>
/// </summary>
/// <param name="sourceVectors">The <see cref="BufferPointer{T}"/> to the source vectors.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceVectors">The <see cref="BufferSpan{T}"/> to the source vectors.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromVector4(
BufferPointer<Vector4> sourceVectors,
BufferPointer<TColor> destColors,
BufferSpan<Vector4> sourceVectors,
BufferSpan<TColor> destColors,
int count)
{
Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset;
@ -55,12 +55,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToVector4()"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the destination vectors.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the destination vectors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToVector4(
BufferPointer<TColor> sourceColors,
BufferPointer<Vector4> destVectors,
BufferSpan<TColor> sourceColors,
BufferSpan<Vector4> destVectors,
int count)
{
byte* sp = (byte*)sourceColors;
@ -78,12 +78,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyz"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -102,15 +102,15 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToXyzBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzBytes(BufferPointer<TColor> sourceColors, BufferPointer<byte> destBytes, int count)
internal virtual void ToXyzBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3)
for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzBytes(dest, i);
@ -121,12 +121,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyzw"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzwBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -145,18 +145,18 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToXyzwBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzwBytes(
BufferPointer<TColor> sourceColors,
BufferPointer<byte> destBytes,
BufferSpan<TColor> sourceColors,
BufferSpan<byte> destBytes,
int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4)
for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzwBytes(dest, i);
@ -167,12 +167,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyx"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -191,15 +191,15 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToZyxBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxBytes(BufferPointer<TColor> sourceColors, BufferPointer<byte> destBytes, int count)
internal virtual void ToZyxBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3)
for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxBytes(dest, i);
@ -210,12 +210,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyxw"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxwBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -234,18 +234,18 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToZyxwBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxwBytes(
BufferPointer<TColor> sourceColors,
BufferPointer<byte> destBytes,
BufferSpan<TColor> sourceColors,
BufferSpan<byte> destBytes,
int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4)
for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxwBytes(dest, i);

20
src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs

@ -127,8 +127,8 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
}
@ -143,8 +143,8 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
}
@ -161,8 +161,8 @@ namespace ImageSharp
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -177,8 +177,8 @@ namespace ImageSharp
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = 255;
}
@ -237,8 +237,8 @@ namespace ImageSharp
// Clamp the value between min and max values
// Round rather than truncate.
uint word2 = (uint)((int)(float)Math.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF);
uint word1 = (uint)(((int)(float)Math.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10);
uint word2 = (uint)((int)MathF.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF);
uint word1 = (uint)(((int)MathF.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10);
return word2 | word1;
}

36
src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs

@ -135,9 +135,9 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
@ -151,10 +151,10 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -168,9 +168,9 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -184,10 +184,10 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -231,10 +231,10 @@ namespace ImageSharp
const float MinNeg = -MaxPos;
// Clamp the value between min and max values
ulong word4 = ((ulong)(float)Math.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x00;
ulong word3 = ((ulong)(float)Math.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10;
ulong word2 = ((ulong)(float)Math.Round(z * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x20;
ulong word1 = ((ulong)(float)Math.Round(w * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x30;
ulong word4 = ((ulong)MathF.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x00;
ulong word3 = ((ulong)MathF.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10;
ulong word2 = ((ulong)MathF.Round(z * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x20;
ulong word1 = ((ulong)MathF.Round(w * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x30;
return word4 | word3 | word2 | word1;
}

28
src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs

@ -109,9 +109,9 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
@ -120,10 +120,10 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -132,9 +132,9 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -143,10 +143,10 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

28
src/ImageSharp/Colors/PackedPixel/Rgba64.cs

@ -108,9 +108,9 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
@ -119,10 +119,10 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -131,9 +131,9 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -142,10 +142,10 @@ namespace ImageSharp
{
Vector4 vector = this.ToVector4() * 255F;
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

16
src/ImageSharp/Colors/PackedPixel/Short2.cs

@ -125,8 +125,8 @@ namespace ImageSharp
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
}
@ -141,8 +141,8 @@ namespace ImageSharp
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
}
@ -159,8 +159,8 @@ namespace ImageSharp
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -175,8 +175,8 @@ namespace ImageSharp
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = 255;
}

28
src/ImageSharp/Colors/PackedPixel/Short4.cs

@ -131,9 +131,9 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
}
/// <inheritdoc />
@ -147,10 +147,10 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.X);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -164,9 +164,9 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
}
/// <inheritdoc />
@ -180,10 +180,10 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
bytes[startIndex] = (byte)MathF.Round(vector.Z);
bytes[startIndex + 1] = (byte)MathF.Round(vector.Y);
bytes[startIndex + 2] = (byte)MathF.Round(vector.X);
bytes[startIndex + 3] = (byte)MathF.Round(vector.W);
}
/// <inheritdoc />

8
src/ImageSharp/Colors/Spaces/CieLab.cs

@ -95,11 +95,11 @@ namespace ImageSharp.Colors.Spaces
// y /= 1F;
z /= 1.08883F;
x = x > 0.008856F ? (float)Math.Pow(x, 0.3333333F) : ((903.3F * x) + 16F) / 116F;
y = y > 0.008856F ? (float)Math.Pow(y, 0.3333333F) : ((903.3F * y) + 16F) / 116F;
z = z > 0.008856F ? (float)Math.Pow(z, 0.3333333F) : ((903.3F * z) + 16F) / 116F;
x = x > 0.008856F ? MathF.Pow(x, 0.3333333F) : ((903.3F * x) + 16F) / 116F;
y = y > 0.008856F ? MathF.Pow(y, 0.3333333F) : ((903.3F * y) + 16F) / 116F;
z = z > 0.008856F ? MathF.Pow(z, 0.3333333F) : ((903.3F * z) + 16F) / 116F;
float l = Math.Max(0, (116F * y) - 16F);
float l = MathF.Max(0, (116F * y) - 16F);
float a = 500F * (x - y);
float b = 200F * (y - z);

4
src/ImageSharp/Colors/Spaces/Cmyk.cs

@ -93,9 +93,9 @@ namespace ImageSharp.Colors.Spaces
float m = 1f - (color.G / 255F);
float y = 1f - (color.B / 255F);
float k = Math.Min(c, Math.Min(m, y));
float k = MathF.Min(c, MathF.Min(m, y));
if (Math.Abs(k - 1.0f) <= Constants.Epsilon)
if (MathF.Abs(k - 1.0f) <= Constants.Epsilon)
{
return new Cmyk(0, 0, 0, 1);
}

12
src/ImageSharp/Colors/Spaces/Hsl.cs

@ -83,27 +83,27 @@ namespace ImageSharp.Colors.Spaces
float g = color.G / 255F;
float b = color.B / 255F;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
float max = MathF.Max(r, MathF.Max(g, b));
float min = MathF.Min(r, MathF.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float l = (max + min) / 2;
if (Math.Abs(chroma) < Constants.Epsilon)
if (MathF.Abs(chroma) < Constants.Epsilon)
{
return new Hsl(0, s, l);
}
if (Math.Abs(r - max) < Constants.Epsilon)
if (MathF.Abs(r - max) < Constants.Epsilon)
{
h = (g - b) / chroma;
}
else if (Math.Abs(g - max) < Constants.Epsilon)
else if (MathF.Abs(g - max) < Constants.Epsilon)
{
h = 2 + ((b - r) / chroma);
}
else if (Math.Abs(b - max) < Constants.Epsilon)
else if (MathF.Abs(b - max) < Constants.Epsilon)
{
h = 4 + ((r - g) / chroma);
}

12
src/ImageSharp/Colors/Spaces/Hsv.cs

@ -83,27 +83,27 @@ namespace ImageSharp.Colors.Spaces
float g = color.G / 255F;
float b = color.B / 255F;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
float max = MathF.Max(r, MathF.Max(g, b));
float min = MathF.Min(r, MathF.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float v = max;
if (Math.Abs(chroma) < Constants.Epsilon)
if (MathF.Abs(chroma) < Constants.Epsilon)
{
return new Hsv(0, s, v);
}
if (Math.Abs(r - max) < Constants.Epsilon)
if (MathF.Abs(r - max) < Constants.Epsilon)
{
h = (g - b) / chroma;
}
else if (Math.Abs(g - max) < Constants.Epsilon)
else if (MathF.Abs(g - max) < Constants.Epsilon)
{
h = 2 + ((b - r) / chroma);
}
else if (Math.Abs(b - max) < Constants.Epsilon)
else if (MathF.Abs(b - max) < Constants.Epsilon)
{
h = 4 + ((r - g) / chroma);
}

11
src/ImageSharp/Colors/Vector4BlendTransforms.cs

@ -5,7 +5,6 @@
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
@ -198,13 +197,13 @@ namespace ImageSharp
amount = amount.Clamp(0, 1);
// Santize on zero alpha
if (Math.Abs(backdrop.W) < Constants.Epsilon)
if (MathF.Abs(backdrop.W) < Constants.Epsilon)
{
source.W *= amount;
return source;
}
if (Math.Abs(source.W) < Constants.Epsilon)
if (MathF.Abs(source.W) < Constants.Epsilon)
{
return backdrop;
}
@ -248,7 +247,7 @@ namespace ImageSharp
/// </returns>
private static float BlendSoftLight(float b, float s)
{
return s <= .5F ? ((2F * b * s) + (b * b * (1F - (2F * s)))) : (float)((Math.Sqrt(b) * ((2F * s) - 1F)) + (2F * b * (1F - s)));
return s <= .5F ? ((2F * b * s) + (b * b * (1F - (2F * s)))) : (MathF.Sqrt(b) * ((2F * s) - 1F)) + (2F * b * (1F - s));
}
/// <summary>
@ -261,7 +260,7 @@ namespace ImageSharp
/// </returns>
private static float BlendDodge(float b, float s)
{
return Math.Abs(s - 1F) < Constants.Epsilon ? s : Math.Min(b / (1F - s), 1F);
return MathF.Abs(s - 1F) < Constants.Epsilon ? s : MathF.Min(b / (1F - s), 1F);
}
/// <summary>
@ -274,7 +273,7 @@ namespace ImageSharp
/// </returns>
private static float BlendBurn(float b, float s)
{
return Math.Abs(s) < Constants.Epsilon ? s : Math.Max(1F - ((1F - b) / s), 0F);
return MathF.Abs(s) < Constants.Epsilon ? s : MathF.Max(1F - ((1F - b) / s), 0F);
}
/// <summary>

4
src/ImageSharp/Common/Extensions/Vector4Extensions.cs

@ -57,7 +57,7 @@ namespace ImageSharp
return signal * 12.92F;
}
return (1.055F * (float)Math.Pow(signal, 0.41666666F)) - 0.055F;
return (1.055F * MathF.Pow(signal, 0.41666666F)) - 0.055F;
}
/// <summary>
@ -77,7 +77,7 @@ namespace ImageSharp
return signal / 12.92F;
}
return (float)Math.Pow((signal + 0.055F) / 1.055F, 2.4F);
return MathF.Pow((signal + 0.055F) / 1.055F, 2.4F);
}
}
}

22
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -53,13 +53,13 @@ namespace ImageSharp
public static float Gaussian(float x, float sigma)
{
const float Numerator = 1.0f;
float denominator = (float)(Math.Sqrt(2 * Math.PI) * sigma);
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma;
float exponentNumerator = -x * x;
float exponentDenominator = (float)(2 * Math.Pow(sigma, 2));
float left = Numerator / denominator;
float right = (float)Math.Exp(exponentNumerator / exponentDenominator);
float right = MathF.Exp(exponentNumerator / exponentDenominator);
return left * right;
}
@ -110,10 +110,10 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float SinC(float x)
{
if (Math.Abs(x) > Constants.Epsilon)
if (MathF.Abs(x) > Constants.Epsilon)
{
x *= (float)Math.PI;
return Clean((float)Math.Sin(x) / x);
x *= MathF.PI;
return Clean(MathF.Sin(x) / x);
}
return 1.0f;
@ -129,7 +129,7 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DegreesToRadians(float degrees)
{
return degrees * (float)(Math.PI / 180);
return degrees * (MathF.PI / 180);
}
/// <summary>
@ -196,19 +196,19 @@ namespace ImageSharp
switch (channel)
{
case RgbaComponent.R:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon;
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon;
break;
case RgbaComponent.G:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon;
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon;
break;
case RgbaComponent.B:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon;
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon;
break;
default:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon;
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon;
break;
}
@ -297,7 +297,7 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Clean(float x)
{
if (Math.Abs(x) < Constants.Epsilon)
if (MathF.Abs(x) < Constants.Epsilon)
{
return 0F;
}

144
src/ImageSharp/Common/Helpers/MathF.cs

@ -0,0 +1,144 @@
// <copyright file="MathF.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Provides single-precision floating point constants and static methods for trigonometric, logarithmic, and other common mathematical functions.
/// </summary>
// ReSharper disable InconsistentNaming
internal static class MathF
{
/// <summary>
/// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π.
/// </summary>
public const float PI = (float)Math.PI;
/// <summary>Returns the absolute value of a single-precision floating-point number.</summary>
/// <param name="f">A number that is greater than or equal to <see cref="F:System.Single.MinValue" />, but less than or equal to <see cref="F:System.Single.MaxValue" />.</param>
/// <returns>A single-precision floating-point number, x, such that 0 ≤ x ≤<see cref="F:System.Single.MaxValue" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Abs(float f)
{
return Math.Abs(f);
}
/// <summary>Returns the smallest integral value that is greater than or equal to the specified single-precision floating-point number.</summary>
/// <param name="f">A single-precision floating-point number. </param>
/// <returns>The smallest integral value that is greater than or equal to <paramref name="f" />.
/// If <paramref name="f" /> is equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NegativeInfinity" />,
/// or <see cref="F:System.Single.PositiveInfinity" />, that value is returned.
/// Note that this method returns a <see cref="T:System.Single" /> instead of an integral type.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Ceiling(float f)
{
return (float)Math.Ceiling(f);
}
/// <summary>Returns e raised to the specified power.</summary>
/// <param name="f">A number specifying a power.</param>
/// <returns>
/// The number e raised to the power <paramref name="f" />.
/// If <paramref name="f" /> equals <see cref="F:System.Single.NaN" /> or <see cref="F:System.Single.PositiveInfinity" />, that value is returned.
/// If <paramref name="f" /> equals <see cref="F:System.Single.NegativeInfinity" />, 0 is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Exp(float f)
{
return (float)Math.Exp(f);
}
/// <summary>Returns the largest integer less than or equal to the specified single-precision floating-point number.</summary>
/// <param name="f">A single-precision floating-point number. </param>
/// <returns>The largest integer less than or equal to <paramref name="f" />.
/// If <paramref name="f" /> is equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NegativeInfinity" />,
/// or <see cref="F:System.Single.PositiveInfinity" />, that value is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Floor(float f)
{
return (float)Math.Floor(f);
}
/// <summary>Returns the larger of two single-precision floating-point numbers.</summary>
/// <param name="val1">The first of two single-precision floating-point numbers to compare. </param>
/// <param name="val2">The second of two single-precision floating-point numbers to compare. </param>
/// <returns>Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is larger.
/// If <paramref name="val1" />, or <paramref name="val2" />, or both <paramref name="val1" /> and <paramref name="val2" /> are
/// equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NaN" /> is returned.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Max(float val1, float val2)
{
return Math.Max(val1, val2);
}
/// <summary>Returns the smaller of two single-precision floating-point numbers.</summary>
/// <param name="val1">The first of two single-precision floating-point numbers to compare. </param>
/// <param name="val2">The second of two single-precision floating-point numbers to compare. </param>
/// <returns>Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is smaller.
/// If <paramref name="val1" />, <paramref name="val2" />, or both <paramref name="val1" /> and <paramref name="val2" /> are equal
/// to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NaN" /> is returned.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Min(float val1, float val2)
{
return Math.Min(val1, val2);
}
/// <summary>Returns a specified number raised to the specified power.</summary>
/// <param name="x">A single-precision floating-point number to be raised to a power. </param>
/// <param name="y">A single-precision floating-point number that specifies a power. </param>
/// <returns>The number <paramref name="x" /> raised to the power <paramref name="y" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Pow(float x, float y)
{
return (float)Math.Pow(x, y);
}
/// <summary>Rounds a single-precision floating-point value to the nearest integral value.</summary>
/// <param name="f">A single-precision floating-point number to be rounded. </param>
/// <returns>
/// The integer nearest <paramref name="f" />.
/// If the fractional component of <paramref name="f" /> is halfway between two integers, one of which is even and the other odd, then the even number is returned.
/// Note that this method returns a <see cref="T:System.Single" /> instead of an integral type.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Round(float f)
{
return (float)Math.Round(f);
}
/// <summary>Returns the sine of the specified angle.</summary>
/// <param name="f">An angle, measured in radians. </param>
/// <returns>
/// The sine of <paramref name="f" />.
/// If <paramref name="f" /> is equal to <see cref="F:System.Single.NaN" />, <see cref="F:System.Single.NegativeInfinity" />,
/// or <see cref="F:System.Single.PositiveInfinity" />, this method returns <see cref="F:System.Single.NaN" />.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Sin(float f)
{
return (float)Math.Sin(f);
}
/// <summary>Returns the square root of a specified number.</summary>
/// <param name="f">The number whose square root is to be found. </param>
/// <returns>
/// One of the values in the following table.
/// <paramref name="f" /> parameter Return value Zero or positive The positive square root of <paramref name="f" />.
/// Negative <see cref="F:System.Single.NaN" />Equals <see cref="F:System.Single.NaN" />
/// <see cref="F:System.Single.NaN" />Equals <see cref="F:System.Single.PositiveInfinity" />
/// <see cref="F:System.Single.PositiveInfinity" />
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Sqrt(float f)
{
return (float)Math.Sqrt(f);
}
}
}

143
src/ImageSharp/Common/Memory/BufferPointer{T}.cs

@ -1,143 +0,0 @@
// <copyright file="BufferPointer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Provides access to elements in an array from a given position.
/// This type shares many similarities with corefx System.Span&lt;T&gt; but there are significant differences in it's functionalities and semantics:
/// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays
/// - It's possible to retrieve a reference to the array (<see cref="Array"/>) so we can pass it to API-s like <see cref="Marshal.Copy(byte[], int, IntPtr, int)"/>
/// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.)
/// This makes <see cref="BufferPointer{T}"/> an unsafe type!
/// - Currently the arrays provided to BufferPointer need to be pinned. This behaviour could be changed using C#7 features.
/// </summary>
/// <typeparam name="T">The type of elements of the array</typeparam>
internal unsafe struct BufferPointer<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="BufferPointer{T}"/> struct from a pinned array and an offset.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of array</param>
/// <param name="offset">The offset inside the array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer(T[] array, void* pointerToArray, int offset)
{
DebugGuard.NotNull(array, nameof(array));
this.Array = array;
this.Offset = offset;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * offset);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferPointer{T}"/> struct from a pinned array.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the start of 'array'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer(T[] array, void* pointerToArray)
{
DebugGuard.NotNull(array, nameof(array));
this.Array = array;
this.Offset = 0;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
/// Gets the array
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the offset inside <see cref="Array"/>
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// Gets the offset inside <see cref="Array"/> in bytes.
/// </summary>
public int ByteOffset => this.Offset * Unsafe.SizeOf<T>();
/// <summary>
/// Gets the pointer to the offseted array position
/// </summary>
public IntPtr PointerAtOffset { get; private set; }
/// <summary>
/// Convertes <see cref="BufferPointer{T}"/> instance to a raw 'void*' pointer
/// </summary>
/// <param name="bufferPointer">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator void*(BufferPointer<T> bufferPointer)
{
return (void*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to a raw 'byte*' pointer
/// </summary>
/// <param name="bufferPointer">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator byte*(BufferPointer<T> bufferPointer)
{
return (byte*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to <see cref="BufferPointer{Byte}"/>
/// setting it's <see cref="Offset"/> and <see cref="PointerAtOffset"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferPointer<byte>(BufferPointer<T> source)
{
BufferPointer<byte> result = default(BufferPointer<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Offset = source.Offset * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferPointer, beginning at 'offset'.
/// </summary>
/// <param name="offset">The offset in number of elements</param>
/// <returns>The offseted (sliced) BufferPointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer<T> Slice(int offset)
{
BufferPointer<T> result = default(BufferPointer<T>);
result.Array = this.Array;
result.Offset = this.Offset + offset;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * offset);
return result;
}
/// <summary>
/// Clears `count` elements beginning from the pointed position.
/// </summary>
/// <param name="count">The number of elements to clear</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int count)
{
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferPointer.USizeOf<T>(count));
}
else
{
System.Array.Clear(this.Array, this.Offset, count);
}
}
}
}

28
src/ImageSharp/Common/Memory/BufferPointer.cs → src/ImageSharp/Common/Memory/BufferSpan.cs

@ -1,4 +1,4 @@
// <copyright file="BufferPointer.cs" company="James Jackson-South">
// <copyright file="BufferSpan.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -11,9 +11,9 @@ namespace ImageSharp
using System.Runtime.InteropServices;
/// <summary>
/// Utility methods for <see cref="BufferPointer{T}"/>
/// Utility methods for <see cref="BufferSpan{T}"/>
/// </summary>
internal static class BufferPointer
internal static class BufferSpan
{
/// <summary>
/// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size.
@ -24,11 +24,11 @@ namespace ImageSharp
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The input <see cref="BufferPointer{T}"/></param>
/// <param name="destination">The destination <see cref="BufferPointer{T}"/>.</param>
/// <param name="source">The input <see cref="BufferSpan{T}"/></param>
/// <param name="destination">The destination <see cref="BufferSpan{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferPointer<T> source, BufferPointer<T> destination, int count)
public static void Copy<T>(BufferSpan<T> source, BufferSpan<T> destination, int count)
where T : struct
{
CopyImpl(source, destination, count);
@ -42,7 +42,7 @@ namespace ImageSharp
/// <param name="destination">The destination buffer.</param>
/// <param name="countInSource">The number of elements to copy from 'source'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferPointer<T> source, BufferPointer<byte> destination, int countInSource)
public static void Copy<T>(BufferSpan<T> source, BufferSpan<byte> destination, int countInSource)
where T : struct
{
CopyImpl(source, destination, countInSource);
@ -56,14 +56,14 @@ namespace ImageSharp
/// <param name="destination">The destination buffer"/></param>
/// <param name="countInDest">The number of <typeparamref name="T"/> elements to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(BufferPointer<byte> source, BufferPointer<T> destination, int countInDest)
public static unsafe void Copy<T>(BufferSpan<byte> source, BufferSpan<T> destination, int countInDest)
where T : struct
{
int byteCount = SizeOf<T>(countInDest);
if (byteCount > (int)ByteCountThreshold)
{
Marshal.Copy(source.Array, source.Offset, destination.PointerAtOffset, byteCount);
Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount);
}
else
{
@ -93,7 +93,7 @@ namespace ImageSharp
=> (uint)SizeOf<T>(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CopyImpl<T, TDest>(BufferPointer<T> source, BufferPointer<TDest> destination, int count)
private static unsafe void CopyImpl<T, TDest>(BufferSpan<T> source, BufferSpan<TDest> destination, int count)
where T : struct
where TDest : struct
{
@ -103,22 +103,22 @@ namespace ImageSharp
{
if (Unsafe.SizeOf<T>() == sizeof(long))
{
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
{
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
{
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
}

237
src/ImageSharp/Common/Memory/BufferSpan{T}.cs

@ -0,0 +1,237 @@
// <copyright file="BufferSpan{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Represents a contiguous region of a pinned managed array.
/// The array is usually owned by a <see cref="PinnedBuffer{T}"/> instance.
/// </summary>
/// <remarks>
/// <see cref="BufferSpan{T}"/> is very similar to corefx System.Span&lt;T&gt;, and we try to maintain a compatible API.
/// There are several differences though:
/// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays.
/// - It's possible to retrieve a reference to the array (<see cref="Array"/>) so we can pass it to API-s like <see cref="Marshal.Copy(byte[], int, IntPtr, int)"/>
/// - It's possible to retrieve the pinned pointer. This enables optimized (unchecked) unsafe operations.
/// - There is no bounds checking for performance reasons, only in debug mode. This makes <see cref="BufferSpan{T}"/> an unsafe type!
/// </remarks>
/// <typeparam name="T">The type of elements of the array</typeparam>
internal unsafe struct BufferSpan<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array and an start.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of the array</param>
/// <param name="start">The index at which to begin the span.</param>
/// <param name="length">The length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray, int start, int length)
{
GuardArrayAndPointer(array, pointerToArray);
DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start));
DebugGuard.MustBeLessThanOrEqualTo(length, array.Length - start, nameof(length));
this.Array = array;
this.Length = length;
this.Start = start;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * start);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array and an start.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of the array</param>
/// <param name="start">The index at which to begin the span.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray, int start)
{
GuardArrayAndPointer(array, pointerToArray);
DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start));
this.Array = array;
this.Length = array.Length - start;
this.Start = start;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * start);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the start of 'array'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray)
{
GuardArrayAndPointer(array, pointerToArray);
this.Array = array;
this.Start = 0;
this.Length = array.Length;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
/// Gets the backing array
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the length of the <see cref="BufferSpan{T}"/>
/// </summary>
public int Length { get; private set; }
/// <summary>
/// Gets the start inside <see cref="Array"/>
/// </summary>
public int Start { get; private set; }
/// <summary>
/// Gets the start inside <see cref="Array"/> in bytes.
/// </summary>
public int ByteOffset => this.Start * Unsafe.SizeOf<T>();
/// <summary>
/// Gets the pointer to the offseted array position
/// </summary>
public IntPtr PointerAtOffset { get; private set; }
/// <summary>
/// Returns a reference to specified element of the span.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The reference to the specified element</returns>
public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
byte* ptr = (byte*)this.PointerAtOffset + BufferSpan.SizeOf<T>(index);
return ref Unsafe.AsRef<T>(ptr);
}
}
/// <summary>
/// Convertes <see cref="BufferSpan{T}"/> instance to a raw 'void*' pointer
/// </summary>
/// <param name="bufferSpan">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator void*(BufferSpan<T> bufferSpan)
{
return (void*)bufferSpan.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferSpan{T}"/> instance to a raw 'byte*' pointer
/// </summary>
/// <param name="bufferSpan">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator byte*(BufferSpan<T> bufferSpan)
{
return (byte*)bufferSpan.PointerAtOffset;
}
/// <summary>
/// Converts generic <see cref="BufferSpan{T}"/> to a <see cref="BufferSpan{T}"/> of bytes
/// setting it's <see cref="Start"/> and <see cref="PointerAtOffset"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferSpan<byte>(BufferSpan<T> source)
{
BufferSpan<byte> result = default(BufferSpan<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Start = source.Start * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
/// </summary>
/// <param name="start">TThe index at which to begin this slice.</param>
/// <returns>The offseted (sliced) BufferSpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan<T> Slice(int start)
{
DebugGuard.MustBeLessThan(start, this.Length, nameof(start));
BufferSpan<T> result = default(BufferSpan<T>);
result.Array = this.Array;
result.Start = this.Start + start;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * start);
result.Length = this.Length - start;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
/// <param name="length">The desired length for the slice (exclusive).</param>
/// <returns>The sliced BufferSpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan<T> Slice(int start, int length)
{
DebugGuard.MustBeLessThanOrEqualTo(start, this.Length, nameof(start));
DebugGuard.MustBeLessThanOrEqualTo(length, this.Length - start, nameof(length));
BufferSpan<T> result = default(BufferSpan<T>);
result.Array = this.Array;
result.Start = this.Start + start;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * start);
result.Length = length;
return result;
}
/// <summary>
/// Clears `count` elements from the beginning of the span.
/// </summary>
/// <param name="count">The number of elements to clear</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int count)
{
DebugGuard.MustBeLessThanOrEqualTo(count, this.Length, nameof(count));
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf<T>(count));
}
else
{
System.Array.Clear(this.Array, this.Start, count);
}
}
/// <summary>
/// Clears the the span
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
this.Clear(this.Length);
}
[Conditional("DEBUG")]
private static void GuardArrayAndPointer(T[] array, void* pointerToArray)
{
DebugGuard.NotNull(array, nameof(array));
DebugGuard.IsFalse(
pointerToArray == (void*)0,
nameof(pointerToArray),
"pointerToArray should not be null pointer!");
}
}
}

2
src/ImageSharp/Common/Memory/Fast2DArray{T}.cs

@ -13,7 +13,7 @@ namespace ImageSharp
/// Provides fast access to 2D arrays.
/// </summary>
/// <typeparam name="T">The type of elements in the array.</typeparam>
public struct Fast2DArray<T>
internal struct Fast2DArray<T>
{
/// <summary>
/// The 1D representation of the 2D array.

31
src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs

@ -0,0 +1,31 @@
// <copyright file="IPinnedImageBuffer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// An interface that represents a pinned buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal interface IPinnedImageBuffer<T>
where T : struct
{
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
/// <summary>
/// Gets a <see cref="BufferSpan{T}"/> to the backing buffer.
/// </summary>
BufferSpan<T> Span { get; }
}
}

91
src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs

@ -11,7 +11,7 @@ namespace ImageSharp
using System.Runtime.InteropServices;
/// <summary>
/// Manages a pinned buffer of value type data 'T' as a Disposable resource.
/// Manages a pinned buffer of value type objects as a Disposable resource.
/// The backing array is either pooled or comes from the outside.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
@ -32,11 +32,11 @@ namespace ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int count)
/// <param name="length">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int length)
{
this.Count = count;
this.Array = PixelDataPool<T>.Rent(count);
this.Length = length;
this.Array = PixelDataPool<T>.Rent(length);
this.isPoolingOwner = true;
this.Pin();
}
@ -47,7 +47,7 @@ namespace ImageSharp
/// <param name="array">The array to pin.</param>
public PinnedBuffer(T[] array)
{
this.Count = array.Length;
this.Length = array.Length;
this.Array = array;
this.isPoolingOwner = false;
this.Pin();
@ -56,16 +56,16 @@ namespace ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The count of "relevant" elements in 'array'.</param>
/// <param name="array">The array to pin.</param>
public PinnedBuffer(int count, T[] array)
/// <param name="length">The count of "relevant" elements in 'array'.</param>
public PinnedBuffer(T[] array, int length)
{
if (array.Length < count)
if (array.Length < length)
{
throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array));
}
this.Count = count;
this.Length = length;
this.Array = array;
this.isPoolingOwner = false;
this.Pin();
@ -85,9 +85,9 @@ namespace ImageSharp
public bool IsDisposedOrLostArrayOwnership { get; private set; }
/// <summary>
/// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when <see cref="Array"/> is pooled.
/// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when <see cref="Array"/> is pooled.
/// </summary>
public int Count { get; private set; }
public int Length { get; private set; }
/// <summary>
/// Gets the backing pinned array.
@ -100,34 +100,71 @@ namespace ImageSharp
public IntPtr Pointer { get; private set; }
/// <summary>
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferPointer{T}"/>.
/// Gets a <see cref="BufferSpan{T}"/> to the backing buffer.
/// </summary>
public BufferSpan<T> Span => this;
/// <summary>
/// Returns a reference to specified element of the buffer.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The reference to the specified element</returns>
public unsafe ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
byte* ptr = (byte*)this.Pointer + BufferSpan.SizeOf<T>(index);
return ref Unsafe.AsRef<T>(ptr);
}
}
/// <summary>
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferSpan{T}"/>.
/// </summary>
/// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator BufferPointer<T>(PinnedBuffer<T> buffer)
public static unsafe implicit operator BufferSpan<T>(PinnedBuffer<T> buffer)
{
return new BufferSpan<T>(buffer.Array, (void*)buffer.Pointer, 0, buffer.Length);
}
/// <summary>
/// Creates a clean instance of <see cref="PinnedBuffer{T}"/> initializing it's elements with 'default(T)'.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
/// <returns>The <see cref="PinnedBuffer{T}"/> instance</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PinnedBuffer<T> CreateClean(int count)
{
return buffer.Slice();
PinnedBuffer<T> buffer = new PinnedBuffer<T>(count);
buffer.Clear();
return buffer;
}
/// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to the beginning of the raw data of the buffer.
/// Gets a <see cref="BufferSpan{T}"/> to an offseted position inside the buffer.
/// </summary>
/// <returns>The <see cref="BufferPointer{T}"/></returns>
/// <param name="start">The start</param>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice()
public unsafe BufferSpan<T> Slice(int start)
{
return new BufferPointer<T>(this.Array, (void*)this.Pointer);
return new BufferSpan<T>(this.Array, (void*)this.Pointer, start, this.Length - start);
}
/// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to an offseted position inside the buffer.
/// Gets a <see cref="BufferSpan{T}"/> to an offseted position inside the buffer.
/// </summary>
/// <param name="offset">The offset</param>
/// <returns>The <see cref="BufferPointer{T}"/></returns>
/// <param name="start">The start</param>
/// <param name="length">The length of the slice</param>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice(int offset)
public unsafe BufferSpan<T> Slice(int start, int length)
{
return new BufferPointer<T>(this.Array, (void*)this.Pointer, offset);
return new BufferSpan<T>(this.Array, (void*)this.Pointer, start, length);
}
/// <summary>
@ -151,7 +188,7 @@ namespace ImageSharp
this.isPoolingOwner = false;
this.Array = null;
this.Count = 0;
this.Length = 0;
GC.SuppressFinalize(this);
}
@ -178,12 +215,12 @@ namespace ImageSharp
}
/// <summary>
/// Clears the buffer, filling elements between 0 and <see cref="Count"/>-1 with default(T)
/// Clears the buffer, filling elements between 0 and <see cref="Length"/>-1 with default(T)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
this.Slice().Clear(this.Count);
((BufferSpan<T>)this).Clear();
}
/// <summary>

45
src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs

@ -0,0 +1,45 @@
// <copyright file="PinnedImageBufferExtensions{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Defines extension methods for <see cref="IPinnedImageBuffer{T}"/>.
/// </summary>
internal static class PinnedImageBufferExtensions
{
/// <summary>
/// Gets a <see cref="BufferSpan{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferSpan<T> GetRowSpan<T>(this IPinnedImageBuffer<T> buffer, int x, int y)
where T : struct
{
return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x);
}
/// <summary>
/// Gets a <see cref="BufferSpan{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferSpan<T> GetRowSpan<T>(this IPinnedImageBuffer<T> buffer, int y)
where T : struct
{
return buffer.Span.Slice(y * buffer.Width, buffer.Width);
}
}
}

78
src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs

@ -0,0 +1,78 @@
// <copyright file="PinnedImageBuffer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents a pinned buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
internal class PinnedImageBuffer<T> : PinnedBuffer<T>, IPinnedImageBuffer<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="PinnedImageBuffer{T}"/> class.
/// </summary>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
public PinnedImageBuffer(int width, int height)
: base(width * height)
{
this.Width = width;
this.Height = height;
}
/// <summary>
/// Initializes a new instance of the <see cref="PinnedImageBuffer{T}"/> class.
/// </summary>
/// <param name="array">The array to pin</param>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
public PinnedImageBuffer(T[] array, int width, int height)
: base(array, width * height)
{
this.Width = width;
this.Height = height;
}
/// <inheritdoc />
public int Width { get; }
/// <inheritdoc />
public int Height { get; }
/// <summary>
/// Gets a reference to the element at the specified position.
/// </summary>
/// <param name="x">The x coordinate (row)</param>
/// <param name="y">The y coordinate (position at row)</param>
/// <returns>A reference to the element.</returns>
public ref T this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return ref this.Array[(this.Width * y) + x];
}
}
/// <summary>
/// Creates a clean instance of <see cref="PinnedImageBuffer{T}"/> initializing it's elements with 'default(T)'.
/// </summary>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
/// <returns>The <see cref="PinnedBuffer{T}"/> instance</returns>
public static PinnedImageBuffer<T> CreateClean(int width, int height)
{
PinnedImageBuffer<T> buffer = new PinnedImageBuffer<T>(width, height);
buffer.Clear();
return buffer;
}
}
}

2
src/ImageSharp/Common/Memory/PixelDataPool{T}.cs

@ -12,7 +12,7 @@ namespace ImageSharp
/// Provides a resource pool that enables reusing instances of value type arrays for image data <see cref="T:T[]"/>.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
public class PixelDataPool<T>
internal class PixelDataPool<T>
where T : struct
{
/// <summary>

27
src/ImageSharp/Configuration.cs

@ -12,6 +12,7 @@ namespace ImageSharp
using System.Threading.Tasks;
using Formats;
using ImageSharp.IO;
/// <summary>
/// Provides initialization code which allows extending the library.
@ -33,6 +34,25 @@ namespace ImageSharp
/// </summary>
private readonly List<IImageFormat> imageFormatsList = new List<IImageFormat>();
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
public Configuration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary>
/// <param name="providers">The inital set of image formats.</param>
public Configuration(params IImageFormat[] providers)
{
foreach (IImageFormat p in providers)
{
this.AddImageFormat(p);
}
}
/// <summary>
/// Gets the default <see cref="Configuration"/> instance.
/// </summary>
@ -53,6 +73,13 @@ namespace ImageSharp
/// </summary>
internal int MaxHeaderSize { get; private set; }
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the fielsystem helper for accessing the local file system.
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
#endif
/// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats.
/// </summary>

23
src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs

@ -5,12 +5,11 @@
namespace ImageSharp.Dithering
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// The base class for performing effor diffusion based dithering.
/// The base class for performing error diffusion based dithering.
/// </summary>
public abstract class ErrorDiffuser : IErrorDiffuser
{
@ -34,19 +33,24 @@ namespace ImageSharp.Dithering
/// </summary>
private readonly int startingOffset;
/// <summary>
/// The diffusion matrix
/// </summary>
private readonly Fast2DArray<float> matrix;
/// <summary>
/// Initializes a new instance of the <see cref="ErrorDiffuser"/> class.
/// </summary>
/// <param name="matrix">The dithering matrix.</param>
/// <param name="divisor">The divisor.</param>
protected ErrorDiffuser(Fast2DArray<float> matrix, byte divisor)
internal ErrorDiffuser(Fast2DArray<float> matrix, byte divisor)
{
Guard.NotNull(matrix, nameof(matrix));
Guard.MustBeGreaterThan(divisor, 0, nameof(divisor));
this.Matrix = matrix;
this.matrixWidth = this.Matrix.Width;
this.matrixHeight = this.Matrix.Height;
this.matrix = matrix;
this.matrixWidth = this.matrix.Width;
this.matrixHeight = this.matrix.Height;
this.divisorVector = new Vector4(divisor);
this.startingOffset = 0;
@ -62,9 +66,6 @@ namespace ImageSharp.Dithering
}
}
/// <inheritdoc />
public Fast2DArray<float> Matrix { get; }
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor transformed, int x, int y, int width, int height)
@ -87,9 +88,9 @@ namespace ImageSharp.Dithering
if (matrixX > 0 && matrixX < width && matrixY > 0 && matrixY < height)
{
float coefficient = this.Matrix[row, col];
float coefficient = this.matrix[row, col];
// Good to disable here as we are not comparing matematical output.
// Good to disable here as we are not comparing mathematical output.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (coefficient == 0)
{

5
src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs

@ -12,11 +12,6 @@ namespace ImageSharp.Dithering
/// </summary>
public interface IErrorDiffuser
{
/// <summary>
/// Gets the dithering matrix
/// </summary>
Fast2DArray<float> Matrix { get; }
/// <summary>
/// Transforms the image applying the dither matrix. This method alters the input pixels array
/// </summary>

17
src/ImageSharp/Dithering/Ordered/Bayer.cs

@ -5,13 +5,11 @@
namespace ImageSharp.Dithering.Ordered
{
using System;
/// <summary>
/// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix.
/// <see href="http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT"/>
/// </summary>
public class Bayer : IOrderedDither
public sealed class Bayer : OrderedDither4x4
{
/// <summary>
/// The threshold matrix.
@ -26,15 +24,12 @@ namespace ImageSharp.Dithering.Ordered
{ 255, 127, 223, 95 }
};
/// <inheritdoc />
public Fast2DArray<byte> Matrix { get; } = ThresholdMatrix;
/// <inheritdoc />
public void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height)
where TColor : struct, IPixel<TColor>
/// <summary>
/// Initializes a new instance of the <see cref="Bayer"/> class.
/// </summary>
public Bayer()
: base(ThresholdMatrix)
{
source.ToXyzwBytes(bytes, 0);
pixels[x, y] = ThresholdMatrix[y % 3, x % 3] >= bytes[index] ? lower : upper;
}
}
}

9
src/ImageSharp/Dithering/Ordered/IOrderedDither.cs

@ -5,18 +5,11 @@
namespace ImageSharp.Dithering
{
using System;
/// <summary>
/// Encapsulates properties and methods required to perfom ordered dithering on an image.
/// </summary>
public interface IOrderedDither
{
/// <summary>
/// Gets the dithering matrix
/// </summary>
Fast2DArray<byte> Matrix { get; }
/// <summary>
/// Transforms the image applying the dither matrix. This method alters the input pixels array
/// </summary>
@ -34,4 +27,4 @@ namespace ImageSharp.Dithering
void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height)
where TColor : struct, IPixel<TColor>;
}
}
}

17
src/ImageSharp/Dithering/Ordered/Ordered.cs

@ -5,13 +5,11 @@
namespace ImageSharp.Dithering.Ordered
{
using System;
/// <summary>
/// Applies error diffusion based dithering using the 4x4 ordered dithering matrix.
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
/// </summary>
public class Ordered : IOrderedDither
public sealed class Ordered : OrderedDither4x4
{
/// <summary>
/// The threshold matrix.
@ -26,15 +24,12 @@ namespace ImageSharp.Dithering.Ordered
{ 240, 112, 208, 80 }
};
/// <inheritdoc />
public Fast2DArray<byte> Matrix { get; } = ThresholdMatrix;
/// <inheritdoc />
public void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height)
where TColor : struct, IPixel<TColor>
/// <summary>
/// Initializes a new instance of the <see cref="Ordered"/> class.
/// </summary>
public Ordered()
: base(ThresholdMatrix)
{
source.ToXyzwBytes(bytes, 0);
pixels[x, y] = ThresholdMatrix[y % 3, x % 3] >= bytes[index] ? lower : upper;
}
}
}

38
src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs

@ -0,0 +1,38 @@
// <copyright file="OrderedDither4x4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Dithering.Ordered
{
/// <summary>
/// The base class for performing ordered ditheroing using a 4x4 matrix.
/// </summary>
public abstract class OrderedDither4x4 : IOrderedDither
{
/// <summary>
/// The dithering matrix
/// </summary>
private Fast2DArray<byte> matrix;
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDither4x4"/> class.
/// </summary>
/// <param name="matrix">The thresholding matrix. </param>
internal OrderedDither4x4(Fast2DArray<byte> matrix)
{
this.matrix = matrix;
}
/// <inheritdoc />
public void Dither<TColor>(PixelAccessor<TColor> pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height)
where TColor : struct, IPixel<TColor>
{
// TODO: This doesn't really cut it for me.
// I'd rather be using float but we need to add some sort of movalization vector methods to all IPixel implementations
// before we can do that as the vectors all cover different ranges.
source.ToXyzwBytes(bytes, 0);
pixels[x, y] = this.matrix[y % 3, x % 3] >= bytes[index] ? lower : upper;
}
}
}

6
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -26,13 +26,13 @@ namespace ImageSharp.Formats
public class BmpDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
new BmpDecoderCore().Decode(image, stream);
return new BmpDecoderCore(configuration).Decode<TColor>(stream);
}
}
}

27
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -43,21 +43,29 @@ namespace ImageSharp.Formats
/// </summary>
private BmpInfoHeader infoHeader;
private Configuration configuration;
/// <summary>
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public BmpDecoderCore(Configuration configuration)
{
this.configuration = configuration;
}
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image, where the data should be set to.
/// Cannot be null (Nothing in Visual Basic).</param>
/// <param name="stream">The stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="image"/> is null.</para>
/// <para>- or -</para>
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
public void Decode<TColor>(Image<TColor> image, Stream stream)
/// <returns>The decoded image.</returns>
public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
this.currentStream = stream;
@ -110,15 +118,14 @@ namespace ImageSharp.Formats
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > image.MaxWidth || this.infoHeader.Height > image.MaxHeight)
if (this.infoHeader.Width > Image<TColor>.MaxWidth || this.infoHeader.Height > Image<TColor>.MaxHeight)
{
throw new ArgumentOutOfRangeException(
$"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ $"bigger then the max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
+ $"bigger then the max allowed size '{Image<TColor>.MaxWidth}x{Image<TColor>.MaxHeight}'");
}
image.InitPixels(this.infoHeader.Width, this.infoHeader.Height);
Image<TColor> image = Image.Create<TColor>(this.infoHeader.Width, this.infoHeader.Height, this.configuration);
using (PixelAccessor<TColor> pixels = image.Lock())
{
switch (this.infoHeader.Compression)
@ -151,6 +158,8 @@ namespace ImageSharp.Formats
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
}
return image;
}
catch (IndexOutOfRangeException e)
{

12
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -14,25 +14,27 @@ namespace ImageSharp.Formats
public class GifDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options);
this.Decode(image, stream, gifOptions);
return this.Decode<TColor>(configuration, stream, gifOptions);
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</param>
public void Decode<TColor>(Image<TColor> image, Stream stream, IGifDecoderOptions options)
/// <returns>The image thats been decoded.</returns>
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IGifDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
new GifDecoderCore<TColor>(options).Decode(image, stream);
return new GifDecoderCore<TColor>(options, configuration).Decode(stream);
}
}
}

50
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -28,9 +28,9 @@ namespace ImageSharp.Formats
private readonly IGifDecoderOptions options;
/// <summary>
/// The image to decode the information to.
/// The global configuration.
/// </summary>
private Image<TColor> decodedImage;
private readonly Configuration configuration;
/// <summary>
/// The currently loaded stream.
@ -67,25 +67,37 @@ namespace ImageSharp.Formats
/// </summary>
private GifGraphicsControlExtension graphicsControlExtension;
/// <summary>
/// The metadata
/// </summary>
private ImageMetaData metaData;
/// <summary>
/// The image to decode the information to.
/// </summary>
private Image<TColor> image;
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TColor}"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public GifDecoderCore(IGifDecoderOptions options)
/// <param name="configuration">The configuration.</param>
public GifDecoderCore(IGifDecoderOptions options, Configuration configuration)
{
this.options = options ?? new GifDecoderOptions();
this.configuration = configuration ?? Configuration.Default;
}
/// <summary>
/// Decodes the stream to the image.
/// </summary>
/// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param>
public void Decode(Image<TColor> image, Stream stream)
/// <returns>The decoded image</returns>
public Image<TColor> Decode(Stream stream)
{
try
{
this.decodedImage = image;
this.metaData = new ImageMetaData();
this.currentStream = stream;
@ -144,6 +156,8 @@ namespace ImageSharp.Formats
ArrayPool<byte>.Shared.Return(this.globalColorTable);
}
}
return this.image;
}
/// <summary>
@ -212,11 +226,13 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
}
if (this.logicalScreenDescriptor.Width > this.decodedImage.MaxWidth || this.logicalScreenDescriptor.Height > this.decodedImage.MaxHeight)
/* // No point doing this as the max width/height is always int.Max and that always bigger than the max size of a gif which is stored in a short.
if (this.logicalScreenDescriptor.Width > Image<TColor>.MaxWidth || this.logicalScreenDescriptor.Height > Image<TColor>.MaxHeight)
{
throw new ArgumentOutOfRangeException(
$"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{this.decodedImage.MaxWidth}x{this.decodedImage.MaxHeight}'");
$"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image<TColor>.MaxWidth}x{Image<TColor>.MaxHeight}'");
}
*/
}
/// <summary>
@ -261,7 +277,7 @@ namespace ImageSharp.Formats
{
this.currentStream.Read(commentsBuffer, 0, length);
string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length);
this.decodedImage.MetaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
finally
{
@ -343,14 +359,14 @@ namespace ImageSharp.Formats
if (this.previousFrame == null)
{
this.decodedImage.MetaData.Quality = colorTableLength / 3;
this.metaData.Quality = colorTableLength / 3;
// This initializes the image to become fully transparent because the alpha channel is zero.
this.decodedImage.InitPixels(imageWidth, imageHeight);
this.image = Image.Create<TColor>(imageWidth, imageHeight, this.metaData, this.configuration);
this.SetFrameDelay(this.decodedImage.MetaData);
this.SetFrameDelay(this.metaData);
image = this.decodedImage;
image = this.image;
}
else
{
@ -368,7 +384,7 @@ namespace ImageSharp.Formats
this.RestoreToBackground(image);
this.decodedImage.Frames.Add(currentFrame);
this.image.Frames.Add(currentFrame);
}
int i = 0;
@ -441,7 +457,7 @@ namespace ImageSharp.Formats
return;
}
this.previousFrame = currentFrame == null ? this.decodedImage.ToFrame() : currentFrame;
this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
@ -462,8 +478,8 @@ namespace ImageSharp.Formats
}
// Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.decodedImage.Width &&
this.restoreArea.Value.Height == this.decodedImage.Height)
if (this.restoreArea.Value.Width == this.image.Width &&
this.restoreArea.Value.Height == this.image.Height)
{
using (PixelAccessor<TColor> pixelAccessor = frame.Lock())
{

5
src/ImageSharp/Formats/IImageDecoder.cs

@ -17,10 +17,11 @@ namespace ImageSharp.Formats
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</param>
void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
/// <returns>The decoded image</returns>
Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>;
}
}

4
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs

@ -71,8 +71,8 @@ namespace ImageSharp.Formats.Jpg
DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2);
var destChannel = decoder.GetDestinationChannel(this.componentIndex);
var destArea = destChannel.GetOffsetedSubAreaForBlock(decodedBlock.Bx, decodedBlock.By);
JpegPixelArea destChannel = decoder.GetDestinationChannel(this.componentIndex);
JpegPixelArea destArea = destChannel.GetOffsetedSubAreaForBlock(decodedBlock.Bx, decodedBlock.By);
destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2);
}

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs

@ -78,7 +78,7 @@ namespace ImageSharp.Formats.Jpg
public static JpegPixelArea CreatePooled(int width, int height)
{
int size = width * height;
var pixels = BytePool.Rent(size);
byte[] pixels = BytePool.Rent(size);
return new JpegPixelArea(pixels, width, 0);
}

4
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs

@ -305,7 +305,7 @@ namespace ImageSharp.Formats.Jpg
/// <param name="scanIndex">The index of the scan</param>
private void DecodeBlock(JpegDecoderCore decoder, int scanIndex)
{
var b = this.pointers.Block;
Block8x8F* b = this.pointers.Block;
int huffmannIdx = (HuffmanTree.AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector;
if (this.ah != 0)
{
@ -630,7 +630,7 @@ namespace ImageSharp.Formats.Jpg
/// <returns>The <see cref="int" /></returns>
private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta)
{
var b = this.pointers.Block;
Block8x8F* b = this.pointers.Block;
for (; zig <= this.zigEnd; zig++)
{
int u = this.pointers.Unzig[zig];

7
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -14,15 +14,14 @@ namespace ImageSharp.Formats
public class JpegDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
using (JpegDecoderCore decoder = new JpegDecoderCore(options))
using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration))
{
decoder.Decode(image, stream, false);
return decoder.Decode<TColor>(stream);
}
}
}

115
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -42,6 +42,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly IDecoderOptions options;
/// <summary>
/// The global configuration
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The App14 marker color-space
/// </summary>
@ -91,8 +96,10 @@ namespace ImageSharp.Formats
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public JpegDecoderCore(IDecoderOptions options)
/// <param name="configuration">The configuration.</param>
public JpegDecoderCore(IDecoderOptions options, Configuration configuration)
{
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new DecoderOptions();
this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
@ -180,18 +187,17 @@ namespace ImageSharp.Formats
/// the data to image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image, where the data should be set to.</param>
/// <param name="stream">The stream, where the image should be.</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param>
public void Decode<TColor>(Image<TColor> image, Stream stream, bool metadataOnly)
/// <returns>The decoded image.</returns>
public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
this.ProcessStream(image, stream, metadataOnly);
if (!metadataOnly)
{
this.ProcessBlocksIntoJpegImageChannels<TColor>();
this.ConvertJpegPixelsToImagePixels(image);
}
ImageMetaData metadata = new ImageMetaData();
this.ProcessStream(metadata, stream, false);
this.ProcessBlocksIntoJpegImageChannels<TColor>();
Image<TColor> image = this.ConvertJpegPixelsToImagePixels<TColor>(metadata);
return image;
}
/// <summary>
@ -276,12 +282,10 @@ namespace ImageSharp.Formats
/// <summary>
/// Read metadata from stream and read the blocks in the scans into <see cref="DecodedBlocks"/>.
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="image">The <see cref="Image{TColor}"/></param>
/// <param name="metadata">The metadata</param>
/// <param name="stream">The stream</param>
/// <param name="metadataOnly">Whether to decode metadata only.</param>
private void ProcessStream<TColor>(Image<TColor> image, Stream stream, bool metadataOnly)
where TColor : struct, IPixel<TColor>
private void ProcessStream(ImageMetaData metadata, Stream stream, bool metadataOnly)
{
this.InputStream = stream;
this.InputProcessor = new InputProcessor(stream, this.Temp);
@ -429,7 +433,7 @@ namespace ImageSharp.Formats
this.ProcessApplicationHeader(remaining);
break;
case JpegConstants.Markers.APP1:
this.ProcessApp1Marker(remaining, image);
this.ProcessApp1Marker(remaining, metadata);
break;
case JpegConstants.Markers.APP14:
this.ProcessApp14Marker(remaining);
@ -496,13 +500,17 @@ namespace ImageSharp.Formats
/// Convert the pixel data in <see cref="YCbCrImage"/> and/or <see cref="JpegPixelArea"/> into pixels of <see cref="Image{TColor}"/>
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="image">The destination image</param>
private void ConvertJpegPixelsToImagePixels<TColor>(Image<TColor> image)
/// <param name="metadata">The metadata for the image.</param>
/// <returns>The decoded image.</returns>
private Image<TColor> ConvertJpegPixelsToImagePixels<TColor>(ImageMetaData metadata)
where TColor : struct, IPixel<TColor>
{
Image<TColor> image = Image.Create<TColor>(this.ImageWidth, this.ImageHeight, metadata, this.configuration);
if (this.grayImage.IsInitialized)
{
this.ConvertFromGrayScale(this.ImageWidth, this.ImageHeight, image);
this.ConvertFromGrayScale(image);
return image;
}
else if (this.ycbcrImage != null)
{
@ -519,27 +527,27 @@ namespace ImageSharp.Formats
// TODO: YCbCrA?
if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck)
{
this.ConvertFromYcck(this.ImageWidth, this.ImageHeight, image);
this.ConvertFromYcck(image);
}
else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
// Assume CMYK
this.ConvertFromCmyk(this.ImageWidth, this.ImageHeight, image);
this.ConvertFromCmyk(image);
}
return;
return image;
}
if (this.ComponentCount == 3)
{
if (this.IsRGB())
{
this.ConvertFromRGB(this.ImageWidth, this.ImageHeight, image);
return;
this.ConvertFromRGB(image);
return image;
}
this.ConvertFromYCbCr(this.ImageWidth, this.ImageHeight, image);
return;
this.ConvertFromYCbCr(image);
return image;
}
throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces.");
@ -582,28 +590,24 @@ namespace ImageSharp.Formats
/// Converts the image from the original CMYK image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromCmyk<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromCmyk<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
y =>
{
// TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte cyan = this.ycbcrImage.YChannel.Pixels[yo + x];
byte magenta = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -623,24 +627,20 @@ namespace ImageSharp.Formats
/// Converts the image from the original grayscale image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromGrayScale<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromGrayScale<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
int yoff = this.grayImage.GetRowOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte rgb = this.grayImage.Pixels[yoff + x];
@ -658,20 +658,17 @@ namespace ImageSharp.Formats
/// Converts the image from the original RBG image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The height.</param>
/// <param name="image">The image.</param>
private void ConvertFromRGB<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromRGB<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
@ -679,7 +676,7 @@ namespace ImageSharp.Formats
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte red = this.ycbcrImage.YChannel.Pixels[yo + x];
byte green = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -699,20 +696,16 @@ namespace ImageSharp.Formats
/// Converts the image from the original YCbCr image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromYCbCr<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromYCbCr<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
image.Configuration.ParallelOptions,
y =>
{
@ -720,7 +713,7 @@ namespace ImageSharp.Formats
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte yy = this.ycbcrImage.YChannel.Pixels[yo + x];
byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -740,28 +733,24 @@ namespace ImageSharp.Formats
/// Converts the image from the original YCCK image pixels.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="image">The image.</param>
private void ConvertFromYcck<TColor>(int width, int height, Image<TColor> image)
private void ConvertFromYcck<TColor>(Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
image.InitPixels(width, height);
using (PixelAccessor<TColor> pixels = image.Lock())
{
Parallel.For(
0,
height,
image.Height,
y =>
{
// TODO: Simplify + optimize + share duplicate code across converter methods
int yo = this.ycbcrImage.GetRowYOffset(y);
int co = this.ycbcrImage.GetRowCOffset(y);
for (int x = 0; x < width; x++)
for (int x = 0; x < image.Width; x++)
{
byte yy = this.ycbcrImage.YChannel.Pixels[yo + x];
byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)];
@ -959,11 +948,9 @@ namespace ImageSharp.Formats
/// <summary>
/// Processes the App1 marker retrieving any stored metadata
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <param name="image">The image.</param>
private void ProcessApp1Marker<TColor>(int remaining, Image<TColor> image)
where TColor : struct, IPixel<TColor>
/// <param name="metadata">The image.</param>
private void ProcessApp1Marker(int remaining, ImageMetaData metadata)
{
if (remaining < 6 || this.options.IgnoreMetadata)
{
@ -978,7 +965,7 @@ namespace ImageSharp.Formats
&& profile[5] == '\0')
{
this.isExif = true;
image.MetaData.ExifProfile = new ExifProfile(profile);
metadata.ExifProfile = new ExifProfile(profile);
}
}

12
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -31,25 +31,27 @@ namespace ImageSharp.Formats
public class PngDecoder : IImageDecoder
{
/// <inheritdoc/>
public void Decode<TColor>(Image<TColor> image, Stream stream, IDecoderOptions options)
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options);
this.Decode(image, stream, pngOptions);
return this.Decode<TColor>(configuration, stream, pngOptions);
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor}"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageBase{TColor}"/> to decode to.</param>
/// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="options">The options for the decoder.</param>
public void Decode<TColor>(Image<TColor> image, Stream stream, IPngDecoderOptions options)
/// <returns>The decoded image.</returns>
public Image<TColor> Decode<TColor>(Configuration configuration, Stream stream, IPngDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
new PngDecoderCore(options).Decode(image, stream);
return new PngDecoderCore(options, configuration).Decode<TColor>(stream);
}
}
}

47
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -74,6 +74,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The stream to decode from.
/// </summary>
@ -134,8 +139,10 @@ namespace ImageSharp.Formats
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public PngDecoderCore(IPngDecoderOptions options)
/// <param name="configuration">The configuration.</param>
public PngDecoderCore(IPngDecoderOptions options, Configuration configuration)
{
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new PngDecoderOptions();
}
@ -148,7 +155,6 @@ namespace ImageSharp.Formats
/// Decodes the stream to the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param>
/// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk.
@ -156,10 +162,11 @@ namespace ImageSharp.Formats
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size.
/// </exception>
public void Decode<TColor>(Image<TColor> image, Stream stream)
/// <returns>The decoded image</returns>
public Image<TColor> Decode<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
Image<TColor> currentImage = image;
ImageMetaData metadata = new ImageMetaData();
this.currentStream = stream;
this.currentStream.Skip(8);
@ -177,7 +184,7 @@ namespace ImageSharp.Formats
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(currentImage, currentChunk.Data);
this.ReadPhysicalChunk(metadata, currentChunk.Data);
break;
case PngChunkTypes.Data:
dataStream.Write(currentChunk.Data, 0, currentChunk.Length);
@ -186,7 +193,7 @@ namespace ImageSharp.Formats
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length);
this.palette = pal;
image.MetaData.Quality = pal.Length / 3;
metadata.Quality = pal.Length / 3;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
@ -194,7 +201,7 @@ namespace ImageSharp.Formats
this.paletteAlpha = alpha;
break;
case PngChunkTypes.Text:
this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length);
this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -208,17 +215,19 @@ namespace ImageSharp.Formats
}
}
if (this.header.Width > image.MaxWidth || this.header.Height > image.MaxHeight)
if (this.header.Width > Image<TColor>.MaxWidth || this.header.Height > Image<TColor>.MaxHeight)
{
throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image<TColor>.MaxWidth}x{Image<TColor>.MaxHeight}'");
}
image.InitPixels(this.header.Width, this.header.Height);
Image<TColor> image = Image.Create<TColor>(this.header.Width, this.header.Height, metadata, this.configuration);
using (PixelAccessor<TColor> pixels = image.Lock())
{
this.ReadScanlines(dataStream, pixels);
}
return image;
}
}
@ -270,18 +279,16 @@ namespace ImageSharp.Formats
/// <summary>
/// Reads the data chunk containing physical dimension data.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to read to.</param>
/// <param name="metadata">The metadata to read to.</param>
/// <param name="data">The data containing physical data.</param>
private void ReadPhysicalChunk<TColor>(Image<TColor> image, byte[] data)
where TColor : struct, IPixel<TColor>
private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data)
{
data.ReverseBytes(0, 4);
data.ReverseBytes(4, 4);
// 39.3700787 = inches in a meter.
image.MetaData.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d;
image.MetaData.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
metadata.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d;
metadata.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
}
/// <summary>
@ -768,12 +775,10 @@ namespace ImageSharp.Formats
/// <summary>
/// Reads a text chunk containing image properties from the data.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The image to decode to.</param>
/// <param name="metadata">The metadata to decode to.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
/// <param name="length">The maximum length to read.</param>
private void ReadTextChunk<TColor>(Image<TColor> image, byte[] data, int length)
where TColor : struct, IPixel<TColor>
private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length)
{
if (this.options.IgnoreMetadata)
{
@ -794,7 +799,7 @@ namespace ImageSharp.Formats
string name = this.options.TextEncoding.GetString(data, 0, zeroIndex);
string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
image.MetaData.Properties.Add(new ImageProperty(name, value));
metadata.Properties.Add(new ImageProperty(name, value));
}
/// <summary>

7
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -544,12 +544,11 @@ namespace ImageSharp.Formats
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="imageBase">The image base.</param>
private void WritePhysicalChunk<TColor>(Stream stream, ImageBase<TColor> imageBase)
/// <param name="image">The image.</param>
private void WritePhysicalChunk<TColor>(Stream stream, Image<TColor> image)
where TColor : struct, IPixel<TColor>
{
Image<TColor> image = imageBase as Image<TColor>;
if (image != null && image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0)
if (image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0)
{
// 39.3700787 = inches in a meter.
int dpmX = (int)Math.Round(image.MetaData.HorizontalResolution * 39.3700787D);

4
src/ImageSharp/Formats/Png/PngInterlaceMode.cs

@ -13,11 +13,11 @@ namespace ImageSharp.Formats
/// <summary>
/// Non interlaced
/// </summary>
None,
None = 0,
/// <summary>
/// Adam 7 interlacing.
/// </summary>
Adam7
Adam7 = 1
}
}

84
src/ImageSharp/IO/BigEndianBitConverter.cs

@ -6,43 +6,81 @@
namespace ImageSharp.IO
{
/// <summary>
/// Implementation of EndianBitConverter which converts to/from big-endian
/// byte arrays.
/// <remarks>
/// Adapted from Miscellaneous Utility Library <see href="http://jonskeet.uk/csharp/miscutil/" />
/// This product includes software developed by Jon Skeet and Marc Gravell. Contact <see href="mailto:skeet@pobox.com" />, or see
/// <see href="http://www.pobox.com/~skeet/" />.
/// </remarks>
/// Implementation of EndianBitConverter which converts to/from big-endian byte arrays.
/// </summary>
internal sealed class BigEndianBitConverter : EndianBitConverter
{
/// <inheritdoc/>
public override Endianness Endianness => Endianness.BigEndian;
public override Endianness Endianness
{
get { return Endianness.BigEndian; }
}
/// <inheritdoc/>
public override bool IsLittleEndian() => false;
public override bool IsLittleEndian
{
get { return false; }
}
/// <inheritdoc/>
protected internal override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index)
public override void CopyBytes(short value, byte[] buffer, int index)
{
int endOffset = index + bytes - 1;
for (int i = 0; i < bytes; i++)
{
buffer[endOffset - i] = unchecked((byte)(value & 0xff));
value = value >> 8;
}
CheckByteArgument(buffer, index, 2);
buffer[index] = (byte)(value >> 8);
buffer[index + 1] = (byte)value;
}
/// <inheritdoc/>
public override void CopyBytes(int value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 4);
buffer[index] = (byte)(value >> 24);
buffer[index + 1] = (byte)(value >> 16);
buffer[index + 2] = (byte)(value >> 8);
buffer[index + 3] = (byte)value;
}
/// <inheritdoc/>
public override void CopyBytes(long value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 8);
buffer[index] = (byte)(value >> 56);
buffer[index + 1] = (byte)(value >> 48);
buffer[index + 2] = (byte)(value >> 40);
buffer[index + 3] = (byte)(value >> 32);
buffer[index + 4] = (byte)(value >> 24);
buffer[index + 5] = (byte)(value >> 16);
buffer[index + 6] = (byte)(value >> 8);
buffer[index + 7] = (byte)value;
}
/// <inheritdoc/>
public override short ToInt16(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 2);
return (short)((value[startIndex] << 8) | value[startIndex + 1]);
}
/// <inheritdoc/>
public override int ToInt32(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 4);
return (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3];
}
/// <inheritdoc/>
protected internal override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert)
public override long ToInt64(byte[] value, int startIndex)
{
long ret = 0;
for (int i = 0; i < bytesToConvert; i++)
{
ret = unchecked((ret << 8) | buffer[startIndex + i]);
}
CheckByteArgument(value, startIndex, 8);
return ret;
long p1 = (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3];
long p2 = (value[startIndex + 4] << 24) | (value[startIndex + 5] << 16) | (value[startIndex + 6] << 8) | value[startIndex + 7];
return p2 | (p1 << 32);
}
}
}

4
src/ImageSharp/IO/EndianBinaryReader.cs

@ -68,7 +68,7 @@ namespace ImageSharp.IO
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoding, nameof(encoding));
Guard.IsTrue(stream.CanRead, nameof(stream), "Stream isn't readable.");
Guard.IsTrue(stream.CanRead, nameof(stream), "Stream isn't readable");
this.BaseStream = stream;
this.BitConverter = EndianBitConverter.GetConverter(endianness);
@ -510,7 +510,7 @@ namespace ImageSharp.IO
{
if (this.disposed)
{
throw new ObjectDisposedException("EndianBinaryReader");
throw new ObjectDisposedException(nameof(EndianBinaryReader));
}
}

52
src/ImageSharp/IO/EndianBinaryWriter.cs

@ -52,31 +52,12 @@ namespace ImageSharp.IO
/// </param>
public EndianBinaryWriter(Endianness endianness, Stream stream, Encoding encoding)
{
var bitConverter = EndianBitConverter.GetConverter(endianness);
// TODO: Use Guard
if (bitConverter == null)
{
throw new ArgumentNullException("bitConverter");
}
if (stream == null)
{
throw new ArgumentNullException("stream");
}
if (encoding == null)
{
throw new ArgumentNullException("encoding");
}
if (!stream.CanWrite)
{
throw new ArgumentException("Stream isn't writable", "stream");
}
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(stream, nameof(encoding));
Guard.IsTrue(stream.CanWrite, nameof(stream), "Stream isn't writable");
this.BaseStream = stream;
this.BitConverter = bitConverter;
this.BitConverter = EndianBitConverter.GetConverter(endianness);
this.Encoding = encoding;
}
@ -256,13 +237,10 @@ namespace ImageSharp.IO
/// Writes an array of bytes to the stream.
/// </summary>
/// <param name="value">The values to write</param>
/// <exception cref="ArgumentNullException">value is null</exception>
public void Write(byte[] value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
Guard.NotNull(value, nameof(value));
this.WriteInternal(value, value.Length);
}
@ -272,6 +250,7 @@ namespace ImageSharp.IO
/// <param name="value">An array containing the bytes to write</param>
/// <param name="offset">The index of the first byte to write within the array</param>
/// <param name="count">The number of bytes to write</param>
/// <exception cref="ArgumentNullException">value is null</exception>
public void Write(byte[] value, int offset, int count)
{
this.CheckDisposed();
@ -292,12 +271,10 @@ namespace ImageSharp.IO
/// Writes an array of characters to the stream, using the encoding for this writer.
/// </summary>
/// <param name="value">An array containing the characters to write</param>
/// <exception cref="ArgumentNullException">value is null</exception>
public void Write(char[] value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
Guard.NotNull(value, nameof(value));
this.CheckDisposed();
byte[] data = this.Encoding.GetBytes(value, 0, value.Length);
@ -305,16 +282,13 @@ namespace ImageSharp.IO
}
/// <summary>
/// Writes a string to the stream, using the encoding for this writer.
/// Writes a length-prefixed string to the stream, using the encoding for this writer.
/// </summary>
/// <param name="value">The value to write. Must not be null.</param>
/// <exception cref="System.ArgumentNullException">value is null</exception>
/// <exception cref="ArgumentNullException">value is null</exception>
public void Write(string value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
Guard.NotNull(value, nameof(value));
this.CheckDisposed();
byte[] data = this.Encoding.GetBytes(value);
@ -368,7 +342,7 @@ namespace ImageSharp.IO
{
if (this.disposed)
{
throw new ObjectDisposedException("EndianBinaryWriter");
throw new ObjectDisposedException(nameof(EndianBinaryWriter));
}
}

63
src/ImageSharp/IO/EndianBitConverter.Conversion.cs

@ -0,0 +1,63 @@
// <copyright file="EndianBitConverter.Conversion.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System;
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Converts the specified double-precision floating point number to a
/// 64-bit signed integer. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A 64-bit signed integer whose value is equivalent to value.</returns>
public unsafe long DoubleToInt64Bits(double value)
{
return *((long*)&value);
}
/// <summary>
/// Converts the specified 64-bit signed integer to a double-precision
/// floating point number. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A double-precision floating point number whose value is equivalent to value.</returns>
public unsafe double Int64BitsToDouble(long value)
{
return *((double*)&value);
}
/// <summary>
/// Converts the specified single-precision floating point number to a
/// 32-bit signed integer. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A 32-bit signed integer whose value is equivalent to value.</returns>
public unsafe int SingleToInt32Bits(float value)
{
return *((int*)&value);
}
/// <summary>
/// Converts the specified 32-bit signed integer to a single-precision floating point
/// number. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A single-precision floating point number whose value is equivalent to value.</returns>
public unsafe float Int32BitsToSingle(int value)
{
return *((float*)&value);
}
}
}

145
src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs

@ -0,0 +1,145 @@
// <copyright file="EndianBitConverter.CopyBytes.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System;
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Copies the specified 16-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public abstract void CopyBytes(short value, byte[] buffer, int index);
/// <summary>
/// Copies the specified 32-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public abstract void CopyBytes(int value, byte[] buffer, int index);
/// <summary>
/// Copies the specified 64-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public abstract void CopyBytes(long value, byte[] buffer, int index);
/// <summary>
/// Copies the specified 16-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(ushort value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((short)value), buffer, index);
}
/// <summary>
/// Copies the specified 32-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(uint value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((int)value), buffer, index);
}
/// <summary>
/// Copies the specified 64-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(ulong value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((long)value), buffer, index);
}
/// <summary>
/// Copies the specified Boolean value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A Boolean value.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(bool value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 1);
buffer[index] = value ? (byte)1 : (byte)0;
}
/// <summary>
/// Copies the specified Unicode character value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(char value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((short)value), buffer, index);
}
/// <summary>
/// Copies the specified double-precision floating point value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public unsafe void CopyBytes(double value, byte[] buffer, int index)
{
this.CopyBytes(*((long*)&value), buffer, index);
}
/// <summary>
/// Copies the specified single-precision floating point value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public unsafe void CopyBytes(float value, byte[] buffer, int index)
{
this.CopyBytes(*((int*)&value), buffer, index);
}
/// <summary>
/// Copies the specified decimal value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public unsafe void CopyBytes(decimal value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 16);
int* pvalue = (int*)&value;
this.CopyBytes(pvalue[0], buffer, index);
this.CopyBytes(pvalue[1], buffer, index + 4);
this.CopyBytes(pvalue[2], buffer, index + 8);
this.CopyBytes(pvalue[3], buffer, index + 12);
}
}
}

139
src/ImageSharp/IO/EndianBitConverter.GetBytes.cs

@ -0,0 +1,139 @@
// <copyright file="EndianBitConverter.GetBytes.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System;
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Returns the specified 16-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public byte[] GetBytes(short value)
{
byte[] result = new byte[2];
this.CopyBytes(value, result, 0);
return result;
}
/// <summary>
/// Returns the specified 32-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(int value)
{
byte[] result = new byte[4];
this.CopyBytes(value, result, 0);
return result;
}
/// <summary>
/// Returns the specified 64-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(long value)
{
byte[] result = new byte[8];
this.CopyBytes(value, result, 0);
return result;
}
/// <summary>
/// Returns the specified 16-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public byte[] GetBytes(ushort value)
{
return this.GetBytes(unchecked((short)value));
}
/// <summary>
/// Returns the specified 32-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(uint value)
{
return this.GetBytes(unchecked((int)value));
}
/// <summary>
/// Returns the specified 64-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(ulong value)
{
return this.GetBytes(unchecked((long)value));
}
/// <summary>
/// Returns the specified Boolean value as an array of bytes.
/// </summary>
/// <param name="value">A Boolean value.</param>
/// <returns>An array of bytes with length 1.</returns>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetBytes(bool value)
{
return new byte[1] { value ? (byte)1 : (byte)0 };
}
/// <summary>
/// Returns the specified Unicode character value as an array of bytes.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetBytes(char value)
{
return this.GetBytes((short)value);
}
/// <summary>
/// Returns the specified double-precision floating point value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public unsafe byte[] GetBytes(double value)
{
return this.GetBytes(*((long*)&value));
}
/// <summary>
/// Returns the specified single-precision floating point value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public unsafe byte[] GetBytes(float value)
{
return this.GetBytes(*((int*)&value));
}
/// <summary>
/// Returns the specified decimal value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 16.</returns>
public byte[] GetBytes(decimal value)
{
byte[] result = new byte[16];
this.CopyBytes(value, result, 0);
return result;
}
}
}

141
src/ImageSharp/IO/EndianBitConverter.ToType.cs

@ -0,0 +1,141 @@
// <copyright file="EndianBitConverter.ToType.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System;
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit signed integer formed by two bytes beginning at startIndex.</returns>
public abstract short ToInt16(byte[] value, int startIndex);
/// <summary>
/// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit signed integer formed by four bytes beginning at startIndex.</returns>
public abstract int ToInt32(byte[] value, int startIndex);
/// <summary>
/// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit signed integer formed by eight bytes beginning at startIndex.</returns>
public abstract long ToInt64(byte[] value, int startIndex);
/// <summary>
/// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit unsigned integer formed by two bytes beginning at startIndex.</returns>
public ushort ToUInt16(byte[] value, int startIndex)
{
return unchecked((ushort)this.ToInt16(value, startIndex));
}
/// <summary>
/// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit unsigned integer formed by four bytes beginning at startIndex.</returns>
public uint ToUInt32(byte[] value, int startIndex)
{
return unchecked((uint)this.ToInt32(value, startIndex));
}
/// <summary>
/// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit unsigned integer formed by eight bytes beginning at startIndex.</returns>
public ulong ToUInt64(byte[] value, int startIndex)
{
return unchecked((ulong)this.ToInt64(value, startIndex));
}
/// <summary>
/// Returns a Boolean value converted from one byte at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>true if the byte at startIndex in value is nonzero; otherwise, false.</returns>
public bool ToBoolean(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 1);
return value[startIndex] != 0;
}
/// <summary>
/// Returns a Unicode character converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A character formed by two bytes beginning at startIndex.</returns>
public char ToChar(byte[] value, int startIndex)
{
return unchecked((char)this.ToInt16(value, startIndex));
}
/// <summary>
/// Returns a double-precision floating point number converted from eight bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A double precision floating point number formed by eight bytes beginning at startIndex.</returns>
public unsafe double ToDouble(byte[] value, int startIndex)
{
long intValue = this.ToInt64(value, startIndex);
return *((double*)&intValue);
}
/// <summary>
/// Returns a single-precision floating point number converted from four bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A single precision floating point number formed by four bytes beginning at startIndex.</returns>
public unsafe float ToSingle(byte[] value, int startIndex)
{
int intValue = this.ToInt32(value, startIndex);
return *((float*)&intValue);
}
/// <summary>
/// Returns a decimal value converted from sixteen bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A decimal formed by sixteen bytes beginning at startIndex.</returns>
public unsafe decimal ToDecimal(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 16);
decimal result = 0m;
int* presult = (int*)&result;
presult[0] = this.ToInt32(value, startIndex);
presult[1] = this.ToInt32(value, startIndex + 4);
presult[2] = this.ToInt32(value, startIndex + 8);
presult[3] = this.ToInt32(value, startIndex + 12);
return result;
}
}
}

682
src/ImageSharp/IO/EndianBitConverter.cs

@ -6,291 +6,56 @@
namespace ImageSharp.IO
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
/// <summary>
/// Equivalent of <see cref="System.BitConverter"/>, but with either endianness.
/// <remarks>
/// Adapted from Miscellaneous Utility Library <see href="http://jonskeet.uk/csharp/miscutil/"/>
/// This product includes software developed by Jon Skeet and Marc Gravell. Contact <see href="mailto:skeet@pobox.com"/>, or see
/// <see href="http://www.pobox.com/~skeet/"/>.
/// </remarks>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "Reviewed. Suppression is OK here. Better readability.")]
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "Reviewed. Suppression is OK here. Better readability.")]
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "Reviewed. Suppression is OK here. Better readability.")]
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:DoNotUseRegions", Justification = "Reviewed. Suppression is OK here. Better readability.")]
internal abstract class EndianBitConverter
internal abstract partial class EndianBitConverter
{
#region Endianness of this converter
/// <summary>
/// Indicates the byte order ("endianness") in which data is converted using this class.
/// The little-endian bit converter.
/// </summary>
/// <remarks>
/// Different computer architectures store data using different byte orders. "Big-endian"
/// means the most significant byte is on the left end of a word. "Little-endian" means the
/// most significant byte is on the right end of a word.
/// </remarks>
/// <returns>true if this converter is little-endian, false otherwise.</returns>
public abstract bool IsLittleEndian();
public static readonly LittleEndianBitConverter LittleEndianConverter = new LittleEndianBitConverter();
/// <summary>
/// Gets the byte order ("endianness") in which data is converted using this class.
/// The big-endian bit converter.
/// </summary>
public abstract Endianness Endianness { get; }
#endregion
#region Factory properties
public static readonly BigEndianBitConverter BigEndianConverter = new BigEndianBitConverter();
/// <summary>
/// The little-endian bit converter.
/// Gets the byte order ("endianness") in which data is converted using this class.
/// </summary>
private static readonly LittleEndianBitConverter LittleConverter = new LittleEndianBitConverter();
public abstract Endianness Endianness { get; }
/// <summary>
/// The big-endian bit converter.
/// Gets a value indicating whether the byte order ("endianness") in which data is converted is little endian.
/// </summary>
private static readonly BigEndianBitConverter BigConverter = new BigEndianBitConverter();
/// <remarks>
/// Different computer architectures store data using different byte orders. "Big-endian"
/// means the most significant byte is on the left end of a word. "Little-endian" means the
/// most significant byte is on the right end of a word.
/// </remarks>
public abstract bool IsLittleEndian { get; }
/// <summary>
/// Gets the converter.
/// </summary>
/// <param name="endianness">The endianness.</param>
/// <returns>an <see cref="EndianBitConverter"/></returns>
/// <exception cref="System.ArgumentException">Not a valid form of Endianness - endianness</exception>
internal static EndianBitConverter GetConverter(Endianness endianness)
/// <exception cref="ArgumentException">Not a valid form of Endianness - endianness</exception>
public static EndianBitConverter GetConverter(Endianness endianness)
{
switch (endianness)
{
case Endianness.LittleEndian:
return LittleConverter;
return LittleEndianConverter;
case Endianness.BigEndian:
return BigConverter;
return BigEndianConverter;
default:
throw new ArgumentException("Not a valid form of Endianness", nameof(endianness));
}
}
#endregion
#region Double/primitive conversions
/// <summary>
/// Converts the specified double-precision floating point number to a
/// 64-bit signed integer. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A 64-bit signed integer whose value is equivalent to value.</returns>
public long DoubleToInt64Bits(double value)
{
return BitConverter.DoubleToInt64Bits(value);
}
/// <summary>
/// Converts the specified 64-bit signed integer to a double-precision
/// floating point number. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A double-precision floating point number whose value is equivalent to value.</returns>
public double Int64BitsToDouble(long value)
{
return BitConverter.Int64BitsToDouble(value);
}
/// <summary>
/// Converts the specified single-precision floating point number to a
/// 32-bit signed integer. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A 32-bit signed integer whose value is equivalent to value.</returns>
public int SingleToInt32Bits(float value)
{
return new Int32SingleUnion(value).AsInt32;
}
/// <summary>
/// Converts the specified 32-bit signed integer to a single-precision floating point
/// number. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A single-precision floating point number whose value is equivalent to value.</returns>
public float Int32BitsToSingle(int value)
{
return new Int32SingleUnion(value).AsSingle;
}
#endregion
#region To(PrimitiveType) conversions
/// <summary>
/// Returns a Boolean value converted from one byte at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>true if the byte at startIndex in value is nonzero; otherwise, false.</returns>
public bool ToBoolean(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 1);
return BitConverter.ToBoolean(value, startIndex);
}
/// <summary>
/// Returns a Unicode character converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A character formed by two bytes beginning at startIndex.</returns>
public char ToChar(byte[] value, int startIndex)
{
return unchecked((char)this.CheckedFromBytes(value, startIndex, 2));
}
/// <summary>
/// Returns a double-precision floating point number converted from eight bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A double precision floating point number formed by eight bytes beginning at startIndex.</returns>
public double ToDouble(byte[] value, int startIndex)
{
return this.Int64BitsToDouble(this.ToInt64(value, startIndex));
}
/// <summary>
/// Returns a single-precision floating point number converted from four bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A single precision floating point number formed by four bytes beginning at startIndex.</returns>
public float ToSingle(byte[] value, int startIndex)
{
return this.Int32BitsToSingle(this.ToInt32(value, startIndex));
}
/// <summary>
/// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit signed integer formed by two bytes beginning at startIndex.</returns>
public short ToInt16(byte[] value, int startIndex)
{
return unchecked((short)this.CheckedFromBytes(value, startIndex, 2));
}
/// <summary>
/// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit signed integer formed by four bytes beginning at startIndex.</returns>
public int ToInt32(byte[] value, int startIndex)
{
return unchecked((int)this.CheckedFromBytes(value, startIndex, 4));
}
/// <summary>
/// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit signed integer formed by eight bytes beginning at startIndex.</returns>
public long ToInt64(byte[] value, int startIndex)
{
return this.CheckedFromBytes(value, startIndex, 8);
}
/// <summary>
/// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit unsigned integer formed by two bytes beginning at startIndex.</returns>
public ushort ToUInt16(byte[] value, int startIndex)
{
return unchecked((ushort)this.CheckedFromBytes(value, startIndex, 2));
}
/// <summary>
/// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit unsigned integer formed by four bytes beginning at startIndex.</returns>
public uint ToUInt32(byte[] value, int startIndex)
{
return unchecked((uint)this.CheckedFromBytes(value, startIndex, 4));
}
/// <summary>
/// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit unsigned integer formed by eight bytes beginning at startIndex.</returns>
public ulong ToUInt64(byte[] value, int startIndex)
{
return unchecked((ulong)this.CheckedFromBytes(value, startIndex, 8));
}
/// <summary>
/// Convert the given number of bytes from the given array, from the given start
/// position, into a long, using the bytes as the least significant part of the long.
/// By the time this is called, the arguments have been checked for validity.
/// </summary>
/// <param name="value">The bytes to convert</param>
/// <param name="startIndex">The index of the first byte to convert</param>
/// <param name="bytesToConvert">The number of bytes to use in the conversion</param>
/// <returns>The converted number</returns>
protected internal abstract long FromBytes(byte[] value, int startIndex, int bytesToConvert);
/// <summary>
/// Checks the given argument for validity.
/// </summary>
/// <param name="value">The byte array passed in</param>
/// <param name="startIndex">The start index passed in</param>
/// <param name="bytesRequired">The number of bytes required</param>
/// <exception cref="System.ArgumentNullException">value is a null reference</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value minus bytesRequired.
/// </exception>
[SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "Keeps code DRY")]
private static void CheckByteArgument(byte[] value, int startIndex, int bytesRequired)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (startIndex < 0 || startIndex > value.Length - bytesRequired)
{
throw new ArgumentOutOfRangeException(nameof(startIndex));
}
}
/// <summary>
/// Checks the arguments for validity before calling FromBytes
/// (which can therefore assume the arguments are valid).
/// </summary>
/// <param name="value">The bytes to convert after checking</param>
/// <param name="startIndex">The index of the first byte to convert</param>
/// <param name="bytesToConvert">The number of bytes to convert</param>
/// <returns>The <see cref="long"/></returns>
private long CheckedFromBytes(byte[] value, int startIndex, int bytesToConvert)
{
CheckByteArgument(value, startIndex, bytesToConvert);
return this.FromBytes(value, startIndex, bytesToConvert);
}
#endregion
#region ToString conversions
/// <summary>
/// Returns a String converted from the elements of a byte array.
@ -336,406 +101,29 @@ namespace ImageSharp.IO
{
return BitConverter.ToString(value, startIndex, length);
}
#endregion
#region Decimal conversions
/// <summary>
/// Returns a decimal value converted from sixteen bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A decimal formed by sixteen bytes beginning at startIndex.</returns>
public decimal ToDecimal(byte[] value, int startIndex)
{
// HACK: This always assumes four parts, each in their own endianness,
// starting with the first part at the start of the byte array.
// On the other hand, there's no real format specified...
int[] parts = new int[4];
for (int i = 0; i < 4; i++)
{
parts[i] = this.ToInt32(value, startIndex + (i * 4));
}
return new decimal(parts);
}
/// <summary>
/// Returns the specified decimal value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 16.</returns>
public byte[] GetBytes(decimal value)
{
byte[] bytes = new byte[16];
int[] parts = decimal.GetBits(value);
for (int i = 0; i < 4; i++)
{
this.CopyBytesImpl(parts[i], 4, bytes, i * 4);
}
return bytes;
}
/// <summary>
/// Copies the specified decimal value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(decimal value, byte[] buffer, int index)
{
int[] parts = decimal.GetBits(value);
for (int i = 0; i < 4; i++)
{
this.CopyBytesImpl(parts[i], 4, buffer, (i * 4) + index);
}
}
#endregion
#region GetBytes conversions
/// <summary>
/// Returns an array with the given number of bytes formed
/// from the least significant bytes of the specified value.
/// This is used to implement the other GetBytes methods.
/// </summary>
/// <param name="value">The value to get bytes for</param>
/// <param name="bytes">The number of significant bytes to return</param>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private byte[] GetBytes(long value, int bytes)
{
byte[] buffer = new byte[bytes];
this.CopyBytes(value, bytes, buffer, 0);
return buffer;
}
/// <summary>
/// Returns the specified Boolean value as an array of bytes.
/// </summary>
/// <param name="value">A Boolean value.</param>
/// <returns>An array of bytes with length 1.</returns>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetBytes(bool value)
{
return BitConverter.GetBytes(value);
}
/// <summary>
/// Returns the specified Unicode character value as an array of bytes.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetBytes(char value)
{
return this.GetBytes(value, 2);
}
/// <summary>
/// Returns the specified double-precision floating point value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(double value)
{
return this.GetBytes(this.DoubleToInt64Bits(value), 8);
}
/// <summary>
/// Returns the specified 16-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public byte[] GetBytes(short value)
{
return this.GetBytes(value, 2);
}
/// <summary>
/// Returns the specified 32-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(int value)
{
return this.GetBytes(value, 4);
}
/// <summary>
/// Returns the specified 64-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(long value)
{
return this.GetBytes(value, 8);
}
/// <summary>
/// Returns the specified single-precision floating point value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(float value)
{
return this.GetBytes(this.SingleToInt32Bits(value), 4);
}
/// <summary>
/// Returns the specified 16-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public byte[] GetBytes(ushort value)
{
return this.GetBytes(value, 2);
}
/// <summary>
/// Returns the specified 32-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(uint value)
{
return this.GetBytes(value, 4);
}
/// <summary>
/// Returns the specified 64-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(ulong value)
{
return this.GetBytes(unchecked((long)value), 8);
}
#endregion
#region CopyBytes conversions
/// <summary>
/// Copies the given number of bytes from the least-specific
/// end of the specified value into the specified byte array, beginning
/// at the specified index.
/// This is used to implement the other CopyBytes methods.
/// </summary>
/// <param name="value">The value to copy bytes for</param>
/// <param name="bytes">The number of significant bytes to copy</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
private void CopyBytes(long value, int bytes, byte[] buffer, int index)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer), "Byte array must not be null");
}
if (buffer.Length < index + bytes)
{
throw new ArgumentOutOfRangeException(nameof(buffer), "Buffer not big enough for value");
}
this.CopyBytesImpl(value, bytes, buffer, index);
}
/// <summary>
/// Copies the given number of bytes from the least-specific
/// end of the specified value into the specified byte array, beginning
/// at the specified index.
/// This must be implemented in concrete derived classes, but the implementation
/// may assume that the value will fit into the buffer.
/// </summary>
/// <param name="value">The value to copy bytes for</param>
/// <param name="bytes">The number of significant bytes to copy</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
protected internal abstract void CopyBytesImpl(long value, int bytes, byte[] buffer, int index);
/// <summary>
/// Copies the specified Boolean value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A Boolean value.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(bool value, byte[] buffer, int index)
{
this.CopyBytes(value ? 1 : 0, 1, buffer, index);
}
/// <summary>
/// Copies the specified Unicode character value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(char value, byte[] buffer, int index)
{
this.CopyBytes(value, 2, buffer, index);
}
/// <summary>
/// Copies the specified double-precision floating point value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(double value, byte[] buffer, int index)
{
this.CopyBytes(this.DoubleToInt64Bits(value), 8, buffer, index);
}
/// <summary>
/// Copies the specified 16-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(short value, byte[] buffer, int index)
{
this.CopyBytes(value, 2, buffer, index);
}
/// <summary>
/// Copies the specified 32-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(int value, byte[] buffer, int index)
{
this.CopyBytes(value, 4, buffer, index);
}
/// <summary>
/// Copies the specified 64-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(long value, byte[] buffer, int index)
{
this.CopyBytes(value, 8, buffer, index);
}
/// <summary>
/// Copies the specified single-precision floating point value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(float value, byte[] buffer, int index)
{
this.CopyBytes(this.SingleToInt32Bits(value), 4, buffer, index);
}
/// <summary>
/// Copies the specified 16-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(ushort value, byte[] buffer, int index)
{
this.CopyBytes(value, 2, buffer, index);
}
/// <summary>
/// Copies the specified 32-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(uint value, byte[] buffer, int index)
{
this.CopyBytes(value, 4, buffer, index);
}
/// <summary>
/// Copies the specified 64-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(ulong value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((long)value), 8, buffer, index);
}
#endregion
#region Private struct used for Single/Int32 conversions
/// <summary>
/// Union used solely for the equivalent of DoubleToInt64Bits and vice versa.
/// Checks the given argument for validity.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct Int32SingleUnion
/// <param name="value">The byte array passed in</param>
/// <param name="startIndex">The start index passed in</param>
/// <param name="bytesRequired">The number of bytes required</param>
/// <exception cref="ArgumentNullException">value is a null reference</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value minus bytesRequired.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static void CheckByteArgument(byte[] value, int startIndex, int bytesRequired)
{
/// <summary>
/// Int32 version of the value.
/// </summary>
[FieldOffset(0)]
private readonly int i;
/// <summary>
/// Single version of the value.
/// </summary>
[FieldOffset(0)]
private readonly float f;
/// <summary>
/// Initializes a new instance of the <see cref="Int32SingleUnion"/> struct.
/// </summary>
/// <param name="i">The integer value of the new instance.</param>
internal Int32SingleUnion(int i)
if (value == null)
{
this.f = 0; // Just to keep the compiler happy
this.i = i;
throw new ArgumentNullException(nameof(value));
}
/// <summary>
/// Initializes a new instance of the <see cref="Int32SingleUnion"/> struct.
/// </summary>
/// <param name="f">
/// The floating point value of the new instance.
/// </param>
internal Int32SingleUnion(float f)
if (startIndex < 0 || startIndex > value.Length - bytesRequired)
{
this.i = 0; // Just to keep the compiler happy
this.f = f;
throw new ArgumentOutOfRangeException(nameof(startIndex));
}
/// <summary>
/// Gets the value of the instance as an integer.
/// </summary>
internal int AsInt32 => this.i;
/// <summary>
/// Gets the value of the instance as a floating point number.
/// </summary>
internal float AsSingle => this.f;
}
#endregion
}
}
}

31
src/ImageSharp/IO/IFileSystem.cs

@ -0,0 +1,31 @@
// <copyright file="IFileSystem.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System.IO;
#if !NETSTANDARD1_1
/// <summary>
/// A simple interface representing the filesystem.
/// </summary>
public interface IFileSystem
{
/// <summary>
/// Returns a readable stream as defined by the path.
/// </summary>
/// <param name="path">Path to the file to open.</param>
/// <returns>A stream representing the file to open.</returns>
Stream OpenRead(string path);
/// <summary>
/// Creates or opens a file and returns it as a writeable stream as defined by the path.
/// </summary>
/// <param name="path">Path to the file to open.</param>
/// <returns>A stream representing the file to open.</returns>
Stream Create(string path);
}
#endif
}

80
src/ImageSharp/IO/LittleEndianBitConverter.cs

@ -6,42 +6,78 @@
namespace ImageSharp.IO
{
/// <summary>
/// Implementation of EndianBitConverter which converts to/from little-endian
/// byte arrays.
/// <remarks>
/// Adapted from Miscellaneous Utility Library <see href="http://jonskeet.uk/csharp/miscutil/"/>
/// This product includes software developed by Jon Skeet and Marc Gravell. Contact <see href="mailto:skeet@pobox.com"/>, or see
/// <see href="http://www.pobox.com/~skeet/"/>.
/// </remarks>
/// Implementation of EndianBitConverter which converts to/from little-endian byte arrays.
/// </summary>
internal sealed class LittleEndianBitConverter : EndianBitConverter
{
/// <inheritdoc/>
public override Endianness Endianness => Endianness.LittleEndian;
public override Endianness Endianness
{
get { return Endianness.LittleEndian; }
}
/// <inheritdoc/>
public override bool IsLittleEndian
{
get { return true; }
}
/// <inheritdoc/>
public override void CopyBytes(short value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 2);
buffer[index + 1] = (byte)(value >> 8);
buffer[index] = (byte)value;
}
/// <inheritdoc/>
public override bool IsLittleEndian() => true;
public override void CopyBytes(int value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 4);
buffer[index + 3] = (byte)(value >> 24);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 1] = (byte)(value >> 8);
buffer[index] = (byte)value;
}
/// <inheritdoc/>
protected internal override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index)
public override void CopyBytes(long value, byte[] buffer, int index)
{
for (int i = 0; i < bytes; i++)
{
buffer[i + index] = unchecked((byte)(value & 0xff));
value = value >> 8;
}
CheckByteArgument(buffer, index, 8);
buffer[index + 7] = (byte)(value >> 56);
buffer[index + 6] = (byte)(value >> 48);
buffer[index + 5] = (byte)(value >> 40);
buffer[index + 4] = (byte)(value >> 32);
buffer[index + 3] = (byte)(value >> 24);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 1] = (byte)(value >> 8);
buffer[index] = (byte)value;
}
/// <inheritdoc/>
protected internal override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert)
public unsafe override short ToInt16(byte[] value, int startIndex)
{
long ret = 0;
for (int i = 0; i < bytesToConvert; i++)
{
ret = unchecked((ret << 8) | buffer[startIndex + bytesToConvert - 1 - i]);
}
CheckByteArgument(value, startIndex, 2);
return (short)((value[startIndex + 1] << 8) | value[startIndex]);
}
return ret;
/// <inheritdoc/>
public unsafe override int ToInt32(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 4);
return (value[startIndex + 3] << 24) | (value[startIndex + 2] << 16) | (value[startIndex + 1] << 8) | value[startIndex];
}
/// <inheritdoc/>
public unsafe override long ToInt64(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 8);
long p1 = (value[startIndex + 7] << 24) | (value[startIndex + 6] << 16) | (value[startIndex + 5] << 8) | value[startIndex + 4];
long p2 = (value[startIndex + 3] << 24) | (value[startIndex + 2] << 16) | (value[startIndex + 1] << 8) | value[startIndex];
return p2 | (p1 << 32);
}
}
}

32
src/ImageSharp/IO/LocalFileSystem.cs

@ -0,0 +1,32 @@
// <copyright file="LocalFileSystem.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.IO
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
#if !NETSTANDARD1_1
/// <summary>
/// A wrapper around the local File apis.
/// </summary>
public class LocalFileSystem : IFileSystem
{
/// <inheritdoc/>
public Stream OpenRead(string path)
{
return File.OpenRead(path);
}
/// <inheritdoc/>
public Stream Create(string path)
{
return File.Create(path);
}
}
#endif
}

66
src/ImageSharp/Image.Create.cs

@ -0,0 +1,66 @@
// <copyright file="Image.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.IO;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Create a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images matadata to preload.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <returns>
/// A new <see cref="Image{TColor}"/> unless <typeparamref name="TColor"/> is <see cref="Color"/> in which case it returns <see cref="Image" />
/// </returns>
internal static Image<TColor> Create<TColor>(int width, int height, ImageMetaData metadata, Configuration configuration)
where TColor : struct, IPixel<TColor>
{
if (typeof(TColor) == typeof(Color))
{
return new Image(width, height, metadata, configuration) as Image<TColor>;
}
else
{
return new Image<TColor>(width, height, metadata, configuration);
}
}
/// <summary>
/// Create a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <returns>
/// A new <see cref="Image{TColor}"/> unless <typeparamref name="TColor"/> is <see cref="Color"/> in which case it returns <see cref="Image" />
/// </returns>
internal static Image<TColor> Create<TColor>(int width, int height, Configuration configuration)
where TColor : struct, IPixel<TColor>
{
return Image.Create<TColor>(width, height, null, configuration);
}
}
}

75
src/ImageSharp/Image.Decode.cs

@ -0,0 +1,75 @@
// <copyright file="Image.Decode.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Buffers;
using System.IO;
using System.Linq;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// By reading the header on the provided stream this calculates the images format.
/// </summary>
/// <param name="stream">The image stream to read the header from.</param>
/// <param name="config">The configuration.</param>
/// <returns>The image format or null if none found.</returns>
private static IImageFormat DiscoverFormat(Stream stream, Configuration config)
{
// This is probably a candidate for making into a public API in the future!
int maxHeaderSize = config.MaxHeaderSize;
if (maxHeaderSize <= 0)
{
return null;
}
IImageFormat format;
byte[] header = ArrayPool<byte>.Shared.Rent(maxHeaderSize);
try
{
long startPosition = stream.Position;
stream.Read(header, 0, maxHeaderSize);
stream.Position = startPosition;
format = config.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header));
}
finally
{
ArrayPool<byte>.Shared.Return(header);
}
return format;
}
/// <summary>
/// Decodes the image stream to the current image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream.</param>
/// <param name="options">The options for the decoder.</param>
/// <param name="config">the configuration.</param>
/// <returns>
/// The decoded image
/// </returns>
private static Image<TColor> Decode<TColor>(Stream stream, IDecoderOptions options, Configuration config)
where TColor : struct, IPixel<TColor>
{
IImageFormat format = DiscoverFormat(stream, config);
if (format == null)
{
return null;
}
Image<TColor> img = format.Decoder.Decode<TColor>(config, stream, options);
img.CurrentImageFormat = format;
return img;
}
}
}

212
src/ImageSharp/Image.FromBytes.cs

@ -0,0 +1,212 @@
// <copyright file="Image.FromStream.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.IO;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data)
{
return Load(null, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data, IDecoderOptions options)
{
return Load(null, data, options);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, byte[] data)
{
return Load(config, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data, IImageDecoder decoder)
{
return Load(data, decoder, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, byte[] data, IDecoderOptions options)
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load(config, ms, options);
}
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options)
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load(ms, decoder, options);
}
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, data, options);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The config for the decoder.</param>
/// <param name="data">The byte array containing image data.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, byte[] data)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(config, data, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data, IImageDecoder decoder)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(data, decoder, null);
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte array containing image data.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, byte[] data, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load<TColor>(config, ms, options);
}
}
/// <summary>
/// Loads the image from the given byte array.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="data">The byte array containing image data.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(byte[] data, IImageDecoder decoder, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
using (MemoryStream ms = new MemoryStream(data))
{
return Load<TColor>(ms, decoder, options);
}
}
}
}

214
src/ImageSharp/Image.FromFile.cs

@ -0,0 +1,214 @@
// <copyright file="Image.FromStream.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
#if !NETSTANDARD1_1
using System;
using System.IO;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path)
{
return Load(null, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path, IDecoderOptions options)
{
return Load(null, path, options);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, string path)
{
return Load(config, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path, IImageDecoder decoder)
{
return Load(path, decoder, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options)
{
return new Image(Load<Color>(path, decoder, options));
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, string path, IDecoderOptions options)
{
config = config ?? Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(path))
{
return Load(config, s, options);
}
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, path, options);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The config for the decoder.</param>
/// <param name="path">The file path to the image.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, string path)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(config, path, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path, IImageDecoder decoder)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(path, decoder, null);
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The configuration options.</param>
/// <param name="path">The file path to the image.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, string path, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
config = config ?? Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(path))
{
return Load<TColor>(config, s, options);
}
}
/// <summary>
/// Loads the image from the given file.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="path">The file path to the image.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(string path, IImageDecoder decoder, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
Configuration config = Configuration.Default;
using (Stream s = config.FileSystem.OpenRead(path))
{
return Load<TColor>(s, decoder, options);
}
}
}
#endif
}

246
src/ImageSharp/Image.FromStream.cs

@ -0,0 +1,246 @@
// <copyright file="Image.FromStream.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.IO;
using System.Text;
using Formats;
/// <summary>
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
/// </summary>
public sealed partial class Image
{
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream)
{
return Load(null, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream, IDecoderOptions options)
{
return Load(null, stream, options);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="config">The config for the decoder.</param>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, Stream stream)
{
return Load(config, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream, IImageDecoder decoder)
{
return Load(stream, decoder, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="config">The configuration options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Configuration config, Stream stream, IDecoderOptions options)
{
Image<Color> image = Load<Color>(config, stream, options);
return image as Image ?? new Image(image);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options)
{
Image<Color> image = new Image(Load<Color>(stream, decoder, options));
return image as Image ?? new Image(image);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(null, stream, options);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The config for the decoder.</param>
/// <param name="stream">The stream containing image information.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, Stream stream)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(config, stream, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream, IImageDecoder decoder)
where TColor : struct, IPixel<TColor>
{
return Load<TColor>(stream, decoder, null);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="stream">The stream containing image information.</param>
/// <param name="decoder">The decoder.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Stream stream, IImageDecoder decoder, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
return WithSeekableStream(stream, s => decoder.Decode<TColor>(Configuration.Default, s, options));
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="config">The configuration options.</param>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
/// <returns>The image</returns>
public static Image<TColor> Load<TColor>(Configuration config, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel<TColor>
{
config = config ?? Configuration.Default;
Image<TColor> img = WithSeekableStream(stream, s => Decode<TColor>(stream, options, config));
if (img != null)
{
return img;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
foreach (IImageFormat format in config.ImageFormats)
{
stringBuilder.AppendLine("-" + format);
}
throw new NotSupportedException(stringBuilder.ToString());
}
private static T WithSeekableStream<T>(Stream stream, Func<Stream, T> action)
{
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
if (stream.CanSeek)
{
return action(stream);
}
else
{
// We want to be able to load images from things like HttpContext.Request.Body
using (MemoryStream ms = new MemoryStream())
{
stream.CopyTo(ms);
ms.Position = 0;
return action(stream);
}
}
}
}
}

199
src/ImageSharp/Image.cs

@ -5,6 +5,7 @@
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.IO;
@ -15,7 +16,7 @@ namespace ImageSharp
/// packed into a single unsigned integer value.
/// </summary>
[DebuggerDisplay("Image: {Width}x{Height}")]
public sealed class Image : Image<Color>
public sealed partial class Image : Image<Color>
{
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class
@ -26,201 +27,45 @@ namespace ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
public Image(int width, int height, Configuration configuration = null)
public Image(int width, int height, Configuration configuration)
: base(width, height, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream)
: base(stream, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options)
: base(stream, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, Configuration configuration)
: base(stream, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options, Configuration configuration)
: base(stream, options, configuration)
{
}
#if !NETSTANDARD1_1
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath)
: base(filePath, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options)
: base(filePath, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, Configuration configuration)
: base(filePath, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="filePath">
/// A file path to read image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options, Configuration configuration)
: base(filePath, options, configuration)
{
}
#endif
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes)
: base(bytes, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// Initializes a new instance of the <see cref="Image"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options)
: base(bytes, options, null)
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public Image(int width, int height)
: this(width, height, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// Initializes a new instance of the <see cref="Image"/> class
/// by making a copy from another image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, Configuration configuration)
: base(bytes, null, configuration)
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public Image(Image<Color> other)
: base(other)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class.
/// Initializes a new instance of the <see cref="Image"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The metadata.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options, Configuration configuration)
: base(bytes, options, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image"/> class
/// by making a copy from another image.
/// </summary>
/// <param name="other">The other image, where the clone should be made from.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="other"/> is null.</exception>
public Image(Image other)
: base(other)
internal Image(int width, int height, ImageMetaData metadata, Configuration configuration)
: base(width, height, metadata, configuration)
{
}
}

10
src/ImageSharp/Image/IImageBase.cs

@ -15,16 +15,6 @@ namespace ImageSharp
/// </summary>
Rectangle Bounds { get; }
/// <summary>
/// Gets or sets the maximum allowable width in pixels.
/// </summary>
int MaxWidth { get; set; }
/// <summary>
/// Gets or sets the maximum allowable height in pixels.
/// </summary>
int MaxHeight { get; set; }
/// <summary>
/// Gets the width in pixels.
/// </summary>

10
src/ImageSharp/Image/IImageBase{TColor}.cs

@ -21,16 +21,6 @@ namespace ImageSharp
/// </summary>
TColor[] Pixels { get; }
/// <summary>
/// Sets the size of the pixel array of the image to the given width and height.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
void InitPixels(int width, int height);
/// <summary>
/// Locks the image providing access to the pixels.
/// <remarks>

41
src/ImageSharp/Image/ImageBase{TColor}.cs

@ -18,6 +18,16 @@ namespace ImageSharp
public abstract class ImageBase<TColor> : IImageBase<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>
/// Gets or sets the maximum allowable width in pixels.
/// </summary>
public const int MaxWidth = int.MaxValue;
/// <summary>
/// Gets or sets the maximum allowable height in pixels.
/// </summary>
public const int MaxHeight = int.MaxValue;
/// <summary>
/// The image pixels
/// </summary>
@ -40,7 +50,7 @@ namespace ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
protected ImageBase(Configuration configuration = null)
protected ImageBase(Configuration configuration)
{
this.Configuration = configuration ?? Configuration.Default;
}
@ -56,10 +66,15 @@ namespace ImageSharp
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
protected ImageBase(int width, int height, Configuration configuration = null)
protected ImageBase(int width, int height, Configuration configuration)
: this(configuration)
{
this.Configuration = configuration ?? Configuration.Default;
this.InitPixels(width, height);
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width;
this.Height = height;
this.RentPixels();
this.ClearPixels();
}
@ -73,6 +88,7 @@ namespace ImageSharp
/// Thrown if the given <see cref="ImageBase{TColor}"/> is null.
/// </exception>
protected ImageBase(ImageBase<TColor> other)
: this(other.Configuration)
{
Guard.NotNull(other, nameof(other), "Other image cannot be null.");
@ -90,12 +106,6 @@ namespace ImageSharp
}
}
/// <inheritdoc/>
public int MaxWidth { get; set; } = int.MaxValue;
/// <inheritdoc/>
public int MaxHeight { get; set; } = int.MaxValue;
/// <inheritdoc/>
public TColor[] Pixels => this.pixelBuffer;
@ -139,17 +149,6 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
/// <inheritdoc/>
public void InitPixels(int width, int height)
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.Width = width;
this.Height = height;
this.RentPixels();
}
/// <inheritdoc/>
public PixelAccessor<TColor> Lock()
{

354
src/ImageSharp/Image/Image{TColor}.cs

@ -35,210 +35,20 @@ namespace ImageSharp
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
public Image(int width, int height, Configuration configuration = null)
: base(width, height, configuration)
{
if (!this.Configuration.ImageFormats.Any())
{
throw new InvalidOperationException("No image formats have been configured.");
}
this.CurrentImageFormat = this.Configuration.ImageFormats.First();
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream)
: this(stream, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options)
: this(stream, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, Configuration configuration)
: this(stream, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="stream">
/// The stream containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="stream"/> is null.</exception>
public Image(Stream stream, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(stream, nameof(stream));
this.Load(stream, options);
}
#if !NETSTANDARD1_1
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath)
: this(filePath, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options)
: this(filePath, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, Configuration configuration)
: this(filePath, null, configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="filePath">
/// The file containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="filePath"/> is null.</exception>
public Image(string filePath, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(filePath, nameof(filePath));
using (var fs = File.OpenRead(filePath))
{
this.Load(fs, options);
}
}
#endif
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes)
: this(bytes, null, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options)
: this(bytes, options, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, Configuration configuration)
: this(bytes, null, configuration)
public Image(int width, int height, Configuration configuration)
: this(width, height, new ImageMetaData(), configuration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class.
/// Initializes a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="bytes">
/// The byte array containing image information.
/// </param>
/// <param name="options">
/// The options for the decoder.
/// </param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="bytes"/> is null.</exception>
public Image(byte[] bytes, IDecoderOptions options, Configuration configuration)
: base(configuration)
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
public Image(int width, int height)
: this(width, height, null)
{
Guard.NotNull(bytes, nameof(bytes));
using (MemoryStream stream = new MemoryStream(bytes, false))
{
this.Load(stream, options);
}
}
/// <summary>
@ -270,13 +80,35 @@ namespace ImageSharp
public Image(ImageBase<TColor> other)
: base(other)
{
this.CopyProperties(other);
this.MetaData = new ImageMetaData();
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TColor}"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
internal Image(int width, int height, ImageMetaData metadata, Configuration configuration)
: base(width, height, configuration)
{
if (!this.Configuration.ImageFormats.Any())
{
throw new InvalidOperationException("No image formats have been configured.");
}
this.MetaData = metadata ?? new ImageMetaData();
this.CurrentImageFormat = this.Configuration.ImageFormats.First();
}
/// <summary>
/// Gets the meta data of the image.
/// </summary>
public ImageMetaData MetaData { get; private set; } = new ImageMetaData();
public ImageMetaData MetaData { get; private set; }
/// <summary>
/// Gets the width of the image in inches. It is calculated as the width of the image
@ -348,9 +180,7 @@ namespace ImageSharp
/// <returns>The <see cref="Image{TColor}"/></returns>
public Image<TColor> Save(Stream stream, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
this.CurrentImageFormat.Encoder.Encode(this, stream, options);
return this;
return this.Save(stream, this.CurrentImageFormat?.Encoder, options);
}
/// <summary>
@ -373,10 +203,9 @@ namespace ImageSharp
/// <returns>The <see cref="Image{TColor}"/></returns>
public Image<TColor> Save(Stream stream, IImageFormat format, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(format, nameof(format));
format.Encoder.Encode(this, stream, options);
return this;
return this.Save(stream, format.Encoder, options);
}
/// <summary>
@ -407,13 +236,8 @@ namespace ImageSharp
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
encoder.Encode(this, stream, options);
// Reset to the start of the stream.
if (stream.CanSeek)
{
stream.Position = 0;
}
encoder.Encode(this, stream, options);
return this;
}
@ -446,7 +270,7 @@ namespace ImageSharp
throw new InvalidOperationException($"No image formats have been registered for the file extension '{ext}'.");
}
return this.Save(filePath, format);
return this.Save(filePath, format, options);
}
/// <summary>
@ -472,10 +296,7 @@ namespace ImageSharp
public Image<TColor> Save(string filePath, IImageFormat format, IEncoderOptions options)
{
Guard.NotNull(format, nameof(format));
using (FileStream fs = File.Create(filePath))
{
return this.Save(fs, format);
}
return this.Save(filePath, format.Encoder, options);
}
/// <summary>
@ -501,9 +322,9 @@ namespace ImageSharp
public Image<TColor> Save(string filePath, IImageEncoder encoder, IEncoderOptions options)
{
Guard.NotNull(encoder, nameof(encoder));
using (FileStream fs = File.Create(filePath))
using (Stream fs = this.Configuration.FileSystem.Create(filePath))
{
return this.Save(fs, encoder);
return this.Save(fs, encoder, options);
}
}
#endif
@ -598,103 +419,8 @@ namespace ImageSharp
/// </param>
private void CopyProperties(IImage other)
{
base.CopyProperties(other);
this.CurrentImageFormat = other.CurrentImageFormat;
this.MetaData = new ImageMetaData(other.MetaData);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>
/// <param name="stream">The stream containing image information.</param>
/// <param name="options">The options for the decoder.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the stream is not readable nor seekable.
/// </exception>
private void Load(Stream stream, IDecoderOptions options)
{
if (!this.Configuration.ImageFormats.Any())
{
throw new InvalidOperationException("No image formats have been configured.");
}
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
}
if (stream.CanSeek)
{
if (this.Decode(stream, options))
{
return;
}
}
else
{
// We want to be able to load images from things like HttpContext.Request.Body
using (MemoryStream ms = new MemoryStream())
{
stream.CopyTo(ms);
ms.Position = 0;
if (this.Decode(ms, options))
{
return;
}
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available formats:");
foreach (IImageFormat format in this.Configuration.ImageFormats)
{
stringBuilder.AppendLine("-" + format);
}
throw new NotSupportedException(stringBuilder.ToString());
}
/// <summary>
/// Decodes the image stream to the current image.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="options">The options for the decoder.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
private bool Decode(Stream stream, IDecoderOptions options)
{
int maxHeaderSize = this.Configuration.MaxHeaderSize;
if (maxHeaderSize <= 0)
{
return false;
}
IImageFormat format;
byte[] header = ArrayPool<byte>.Shared.Rent(maxHeaderSize);
try
{
long startPosition = stream.Position;
stream.Read(header, 0, maxHeaderSize);
stream.Position = startPosition;
format = this.Configuration.ImageFormats.FirstOrDefault(x => x.IsSupportedFileFormat(header));
}
finally
{
ArrayPool<byte>.Shared.Return(header);
}
if (format == null)
{
return false;
}
format.Decoder.Decode(this, stream, options);
this.CurrentImageFormat = format;
return true;
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save