Browse Source

Merge from latest master

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
189150f498
  1. 6
      .editorconfig
  2. 1
      .gitignore
  3. 36
      ImageSharp.sln
  4. 4
      README.md
  5. 12
      samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj
  6. 71
      samples/AvatarWithRoundedCorner/Program.cs
  7. 3
      samples/AvatarWithRoundedCorner/fb.jpg
  8. 12
      samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj
  9. 20
      samples/ChangeDefaultEncoderOptions/Program.cs
  10. 1
      src/ImageSharp.Drawing/Brushes/IBrush.cs
  11. 1
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  12. 1
      src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
  13. 1
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
  14. 1
      src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
  15. 1
      src/ImageSharp.Drawing/DrawImage.cs
  16. 2
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  17. 21
      src/ImageSharp.Drawing/Paths/DrawBeziers.cs
  18. 13
      src/ImageSharp.Drawing/Paths/DrawLines.cs
  19. 13
      src/ImageSharp.Drawing/Paths/DrawPolygon.cs
  20. 15
      src/ImageSharp.Drawing/Paths/DrawRectangle.cs
  21. 80
      src/ImageSharp.Drawing/Paths/FillPathBuilder.cs
  22. 9
      src/ImageSharp.Drawing/Paths/FillPolygon.cs
  23. 13
      src/ImageSharp.Drawing/Paths/FillRectangle.cs
  24. 29
      src/ImageSharp.Drawing/Paths/RectangleExtensions.cs
  25. 2
      src/ImageSharp.Drawing/Paths/ShapePath.cs
  26. 30
      src/ImageSharp.Drawing/Paths/ShapeRegion.cs
  27. 1
      src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
  28. 1
      src/ImageSharp.Drawing/Processors/FillProcessor.cs
  29. 1
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  30. 1
      src/ImageSharp.Drawing/Region.cs
  31. 8
      src/ImageSharp.Drawing/Text/DrawText.Path.cs
  32. 29
      src/ImageSharp.Drawing/Text/DrawText.cs
  33. 2
      src/ImageSharp/Common/Exceptions/ImageFormatException.cs
  34. 2
      src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
  35. 1
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  36. 244
      src/ImageSharp/Configuration.cs
  37. 21
      src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
  38. 25
      src/ImageSharp/Formats/Bmp/BmpConstants.cs
  39. 7
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  40. 3
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  41. 25
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  42. 18
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  43. 45
      src/ImageSharp/Formats/Bmp/BmpEncoderOptions.cs
  44. 2
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  45. 30
      src/ImageSharp/Formats/Bmp/BmpFormat.cs
  46. 37
      src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
  47. 2
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  48. 21
      src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
  49. 13
      src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
  50. 37
      src/ImageSharp/Formats/DecoderOptions.cs
  51. 37
      src/ImageSharp/Formats/EncoderOptions.cs
  52. 22
      src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
  53. 15
      src/ImageSharp/Formats/Gif/GifConstants.cs
  54. 33
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  55. 31
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  56. 47
      src/ImageSharp/Formats/Gif/GifDecoderOptions.cs
  57. 44
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  58. 66
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  59. 65
      src/ImageSharp/Formats/Gif/GifEncoderOptions.cs
  60. 32
      src/ImageSharp/Formats/Gif/GifFormat.cs
  61. 41
      src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs
  62. 15
      src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
  63. 25
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  64. 8
      src/ImageSharp/Formats/Gif/ImageExtensions.cs
  65. 18
      src/ImageSharp/Formats/IEncoderOptions.cs
  66. 4
      src/ImageSharp/Formats/IImageDecoder.cs
  67. 4
      src/ImageSharp/Formats/IImageEncoder.cs
  68. 45
      src/ImageSharp/Formats/IImageFormat.cs
  69. 30
      src/ImageSharp/Formats/IImageFormatDetector.cs
  70. 1
      src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs
  71. 14
      src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs
  72. 17
      src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
  73. 8
      src/ImageSharp/Formats/Jpeg/ImageExtensions.cs
  74. 22
      src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs
  75. 12
      src/ImageSharp/Formats/Jpeg/JpegConstants.cs
  76. 12
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  77. 22
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  78. 36
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  79. 52
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  80. 56
      src/ImageSharp/Formats/Jpeg/JpegEncoderOptions.cs
  81. 76
      src/ImageSharp/Formats/Jpeg/JpegFormat.cs
  82. 87
      src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
  83. 15
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  84. 28
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  85. 8
      src/ImageSharp/Formats/Png/ImageExtensions.cs
  86. 21
      src/ImageSharp/Formats/Png/PngConfigurationModule.cs
  87. 31
      src/ImageSharp/Formats/Png/PngConstants.cs
  88. 27
      src/ImageSharp/Formats/Png/PngDecoder.cs
  89. 230
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  90. 49
      src/ImageSharp/Formats/Png/PngDecoderOptions.cs
  91. 61
      src/ImageSharp/Formats/Png/PngEncoder.cs
  92. 80
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  93. 82
      src/ImageSharp/Formats/Png/PngEncoderOptions.cs
  94. 34
      src/ImageSharp/Formats/Png/PngFormat.cs
  95. 2
      src/ImageSharp/Formats/Png/PngHeader.cs
  96. 43
      src/ImageSharp/Formats/Png/PngImageFormatDetector.cs
  97. 2
      src/ImageSharp/Formats/Png/PngInterlaceMode.cs
  98. 2
      src/ImageSharp/Formats/Png/Zlib/IChecksum.cs
  99. 2
      src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
  100. 19
      src/ImageSharp/IConfigurationModule.cs

6
.editorconfig

@ -13,4 +13,8 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
dotnet_style_qualification_for_field = true:warning
dotnet_style_qualification_for_method = true:warning
dotnet_style_qualification_for_property = true:warning
dotnet_style_qualification_for_property = true:warning
[*.tt]
indent_style = space
indent_size = 4

1
.gitignore

@ -217,3 +217,4 @@ artifacts/
#CodeCoverage
**/CodeCoverage/*
docs/
/samples/AvatarWithRoundedCorner/output

36
ImageSharp.sln

@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26403.7
VisualStudioVersion = 15.0.26430.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
@ -43,7 +43,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\I
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Formats.Tiff.Tests", "tests\ImageSharp.Formats.Tiff.Tests\ImageSharp.Formats.Tiff.Tests.csproj", "{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}"
EndProject
@ -141,6 +147,30 @@ Global
{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x64.Build.0 = Release|x64
{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.ActiveCfg = Release|x86
{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.Build.0 = Release|x86
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.Build.0 = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.ActiveCfg = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.Build.0 = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.ActiveCfg = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.Build.0 = Debug|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.Build.0 = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -154,5 +184,7 @@ Global
{2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2}
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2}
EndGlobalSection
EndGlobal

4
README.md

@ -110,7 +110,7 @@ Setting individual pixel values can be perfomed as follows:
```csharp
// Individual pixels
using (Image<Rgba32> image = new Image<Rgba32>(400, 400)
using (Image<Rgba32> image = new Image<Rgba32>(400, 400))
{
image[200, 200] = Rgba32.White;
}
@ -213,4 +213,4 @@ Become a sponsor and get your logo on our README on Github with a link to your s
<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>
<a href="https://opencollective.com/imagesharp/sponsor/29/website" target="_blank"><img src="https://opencollective.com/imagesharp/sponsor/29/avatar.svg"></a>

12
samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />
</ItemGroup>
</Project>

71
samples/AvatarWithRoundedCorner/Program.cs

@ -0,0 +1,71 @@

namespace AvatarWithRoundedCorner
{
using System;
using System.Numerics;
using ImageSharp;
using SixLabors.Primitives;
using SixLabors.Shapes;
class Program
{
static void Main(string[] args)
{
System.IO.Directory.CreateDirectory("output");
GenerateAvatar("fb.jpg", "output/fb.png", new Size(200, 200), 20);
GenerateAvatar("fb.jpg", "output/fb-round.png", new Size(200, 200), 100);
GenerateAvatar("fb.jpg", "output/fb-rounder.png", new Size(200, 200), 150);
}
private static void GenerateAvatar(string source, string destination, Size size, float cornerRadius)
{
using (var image = Image.Load(source))
{
image.Resize(new ImageSharp.Processing.ResizeOptions
{
Size = size,
Mode = ImageSharp.Processing.ResizeMode.Crop
});
ApplyRoundedCourners(image, cornerRadius);
image.Save(destination);
}
}
public static void ApplyRoundedCourners(Image<Rgba32> img, float cornerRadius)
{
var corners = BuildCorners(img.Width, img.Height, cornerRadius);
// now we have our corners time to draw them
img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true)
{
BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background
});
}
public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius)
{
// first create a square
var rect = new SixLabors.Shapes.RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius);
// then cut out of the square a circle so we are left with a corner
var cornerToptLeft = rect.Clip(new SixLabors.Shapes.EllipsePolygon(cornerRadius-0.5f, cornerRadius - 0.5f, cornerRadius));
// corner is now a corner shape positions top left
//lets make 3 more positioned correctly, we cando that by translating the orgional artound the center of the image
var center = new Vector2(imageWidth / 2, imageHeight / 2);
var angle = Math.PI / 2f;
float rightPos = imageWidth - cornerToptLeft.Bounds.Width +1;
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1;
// move it across the widthof the image - the width of the shape
var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0);
var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos);
var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos);
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
}
}
}

3
samples/AvatarWithRoundedCorner/fb.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:93bb4d6281dc1e845db57e836e0dca30b7a4062e81044efb27ad4d8b1a33130c
size 15787

12
samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
</Project>

20
samples/ChangeDefaultEncoderOptions/Program.cs

@ -0,0 +1,20 @@
using System;
using ImageSharp;
using ImageSharp.Formats;
namespace ChangeDefaultEncoderOptions
{
class Program
{
static void Main(string[] args)
{
// lets switch out the default encoder for jpeg to one
// that saves at 90 quality and ignores the matadata
Configuration.Default.SetEncoder(ImageFormats.Jpeg, new ImageSharp.Formats.JpegEncoder()
{
Quality = 90,
IgnoreMetadata = true
});
}
}
}

1
src/ImageSharp.Drawing/Brushes/IBrush.cs

@ -7,6 +7,7 @@ namespace ImageSharp.Drawing
{
using ImageSharp.PixelFormats;
using Processors;
using SixLabors.Primitives;
/// <summary>
/// Brush represents a logical configuration of a brush which can be used to source pixel colors

1
src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs

@ -10,6 +10,7 @@ namespace ImageSharp.Drawing.Brushes
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using Processors;
using SixLabors.Primitives;
/// <summary>
/// Provides an implementation of an image brush for painting images within areas.

1
src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs

@ -11,6 +11,7 @@ namespace ImageSharp.Drawing.Brushes
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using Processors;
using SixLabors.Primitives;
/// <summary>
/// Provides an implementation of a pattern brush for painting patterns.

1
src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs

@ -11,6 +11,7 @@ namespace ImageSharp.Drawing.Brushes
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using Processors;
using SixLabors.Primitives;
/// <summary>
/// Provides an implementation of a brush that can recolor an image

1
src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs

@ -11,6 +11,7 @@ namespace ImageSharp.Drawing.Brushes
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using Processors;
using SixLabors.Primitives;
/// <summary>
/// Provides an implementation of a solid brush for painting solid color areas.

1
src/ImageSharp.Drawing/DrawImage.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{
using Drawing.Processors;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.

2
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -36,7 +36,7 @@
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.Shapes.Text" Version="0.1.0-alpha0017" />
<PackageReference Include="SixLabors.Shapes.Text" Version="0.1.0-alpha0018" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta001">
<PrivateAssets>All</PrivateAssets>
</PackageReference>

21
src/ImageSharp.Drawing/Paths/DrawBeziers.cs

@ -10,6 +10,7 @@ namespace ImageSharp
using Drawing.Brushes;
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;
/// <summary>
@ -27,10 +28,10 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new BezierLineSegment(points)), options);
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new CubicBezierLineSegment(points)), options);
}
/// <summary>
@ -42,10 +43,10 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Vector2[] points)
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new BezierLineSegment(points)));
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new CubicBezierLineSegment(points)));
}
/// <summary>
@ -57,7 +58,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Vector2[] points)
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, TPixel color, float thickness, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points);
@ -73,7 +74,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, TPixel color, float thickness, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points, options);
@ -88,10 +89,10 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new Path(new BezierLineSegment(points)), options);
return source.Draw(pen, new Path(new CubicBezierLineSegment(points)), options);
}
/// <summary>
@ -102,10 +103,10 @@ namespace ImageSharp
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Vector2[] points)
public static Image<TPixel> DrawBeziers<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new Path(new BezierLineSegment(points)));
return source.Draw(pen, new Path(new CubicBezierLineSegment(points)));
}
}
}

13
src/ImageSharp.Drawing/Paths/DrawLines.cs

@ -10,6 +10,7 @@ namespace ImageSharp
using Drawing.Brushes;
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;
/// <summary>
@ -27,7 +28,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new LinearLineSegment(points)), options);
@ -42,7 +43,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Vector2[] points)
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new LinearLineSegment(points)));
@ -57,7 +58,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Vector2[] points)
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, TPixel color, float thickness, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawLines(new SolidBrush<TPixel>(color), thickness, points);
@ -73,7 +74,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>>
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, TPixel color, float thickness, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawLines(new SolidBrush<TPixel>(color), thickness, points, options);
@ -88,7 +89,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new Path(new LinearLineSegment(points)), options);
@ -102,7 +103,7 @@ namespace ImageSharp
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Vector2[] points)
public static Image<TPixel> DrawLines<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new Path(new LinearLineSegment(points)));

13
src/ImageSharp.Drawing/Paths/DrawPolygon.cs

@ -10,6 +10,7 @@ namespace ImageSharp
using Drawing.Brushes;
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;
/// <summary>
@ -27,7 +28,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), new Polygon(new LinearLineSegment(points)), options);
@ -42,7 +43,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Vector2[] points)
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), new Polygon(new LinearLineSegment(points)));
@ -57,7 +58,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Vector2[] points)
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, TPixel color, float thickness, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points);
@ -73,7 +74,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, TPixel color, float thickness, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points, options);
@ -87,7 +88,7 @@ namespace ImageSharp
/// <param name="pen">The pen.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Vector2[] points)
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default);
@ -102,7 +103,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> DrawPolygon<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options);

15
src/ImageSharp.Drawing/Paths/DrawRectangle.cs

@ -9,6 +9,7 @@ namespace ImageSharp
using Drawing.Brushes;
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -24,10 +25,10 @@ namespace ImageSharp
/// <param name="shape">The shape.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Rectangle shape, GraphicsOptions options)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, RectangleF shape, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options);
return source.Draw(pen, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height), options);
}
/// <summary>
@ -38,7 +39,7 @@ namespace ImageSharp
/// <param name="pen">The pen.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, Rectangle shape)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IPen<TPixel> pen, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(pen, shape, GraphicsOptions.Default);
@ -54,7 +55,7 @@ namespace ImageSharp
/// <param name="shape">The shape.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Rectangle shape, GraphicsOptions options)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, RectangleF shape, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), shape, options);
@ -69,7 +70,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, Rectangle shape)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, float thickness, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new Pen<TPixel>(brush, thickness), shape);
@ -85,7 +86,7 @@ namespace ImageSharp
/// <param name="shape">The shape.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Rectangle shape, GraphicsOptions options)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, RectangleF shape, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new SolidBrush<TPixel>(color), thickness, shape, options);
@ -100,7 +101,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, Rectangle shape)
public static Image<TPixel> Draw<TPixel>(this Image<TPixel> source, TPixel color, float thickness, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
{
return source.Draw(new SolidBrush<TPixel>(color), thickness, shape);

80
src/ImageSharp.Drawing/Paths/FillPathBuilder.cs

@ -0,0 +1,80 @@
// <copyright file="FillPaths.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 Drawing;
using Drawing.Brushes;
using ImageSharp.PixelFormats;
using SixLabors.Shapes;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The shape.</param>
/// <param name="options">The graphics options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, Action<PathBuilder> path, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
var pb = new PathBuilder();
path(pb);
return source.Fill(brush, pb.Build(), options);
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush.
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="brush">The brush.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, Action<PathBuilder> path)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(brush, path, GraphicsOptions.Default);
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, Action<PathBuilder> path, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), path, options);
}
/// <summary>
/// Flood fills the image in the shape of the provided polygon with the specified brush..
/// </summary>
/// <typeparam name="TPixel">The type of the color.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color.</param>
/// <param name="path">The path.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, Action<PathBuilder> path)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), path);
}
}
}

9
src/ImageSharp.Drawing/Paths/FillPolygon.cs

@ -10,6 +10,7 @@ namespace ImageSharp
using Drawing;
using Drawing.Brushes;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;
/// <summary>
@ -26,7 +27,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options);
@ -40,7 +41,7 @@ namespace ImageSharp
/// <param name="brush">The brush.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, Vector2[] points)
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(brush, new Polygon(new LinearLineSegment(points)));
@ -55,7 +56,7 @@ namespace ImageSharp
/// <param name="points">The points.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, TPixel color, Vector2[] points, GraphicsOptions options)
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, TPixel color, PointF[] points, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), new Polygon(new LinearLineSegment(points)), options);
@ -69,7 +70,7 @@ namespace ImageSharp
/// <param name="color">The color.</param>
/// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, TPixel color, Vector2[] points)
public static Image<TPixel> FillPolygon<TPixel>(this Image<TPixel> source, TPixel color, PointF[] points)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), new Polygon(new LinearLineSegment(points)));

13
src/ImageSharp.Drawing/Paths/FillRectangle.cs

@ -8,6 +8,7 @@ namespace ImageSharp
using Drawing;
using Drawing.Brushes;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -23,10 +24,10 @@ namespace ImageSharp
/// <param name="shape">The shape.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, Rectangle shape, GraphicsOptions options)
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, RectangleF shape, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options);
return source.Fill(brush, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height), options);
}
/// <summary>
@ -37,10 +38,10 @@ namespace ImageSharp
/// <param name="brush">The brush.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, Rectangle shape)
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, IBrush<TPixel> brush, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height));
return source.Fill(brush, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height));
}
/// <summary>
@ -52,7 +53,7 @@ namespace ImageSharp
/// <param name="shape">The shape.</param>
/// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, Rectangle shape, GraphicsOptions options)
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, RectangleF shape, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), shape, options);
@ -66,7 +67,7 @@ namespace ImageSharp
/// <param name="color">The color.</param>
/// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, Rectangle shape)
public static Image<TPixel> Fill<TPixel>(this Image<TPixel> source, TPixel color, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
{
return source.Fill(new SolidBrush<TPixel>(color), shape);

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

@ -1,29 +0,0 @@
// <copyright file="RectangleExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Drawing
{
using System;
/// <summary>
/// Extension methods for helping to bridge Shaper2D and ImageSharp primitives.
/// </summary>
internal static class RectangleExtensions
{
/// <summary>
/// Converts a Shaper2D <see cref="SixLabors.Shapes.Rectangle"/> to an ImageSharp <see cref="Rectangle"/> by creating a <see cref="Rectangle"/> the entirely surrounds the source.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <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)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);
}
}
}

2
src/ImageSharp.Drawing/Paths/ShapePath.cs

@ -11,8 +11,6 @@ namespace ImageSharp.Drawing
using SixLabors.Shapes;
using Rectangle = ImageSharp.Rectangle;
/// <summary>
/// A mapping between a <see cref="IPath"/> and a region.
/// </summary>

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

@ -8,11 +8,10 @@ namespace ImageSharp.Drawing
using System;
using System.Buffers;
using System.Numerics;
using ImageSharp.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
using Rectangle = ImageSharp.Rectangle;
/// <summary>
/// A mapping between a <see cref="IPath"/> and a region.
/// </summary>
@ -25,7 +24,12 @@ namespace ImageSharp.Drawing
public ShapeRegion(IPath shape)
{
this.Shape = shape.AsClosedPath();
this.Bounds = shape.Bounds.Convert();
int left = (int)MathF.Floor(shape.Bounds.Left);
int top = (int)MathF.Floor(shape.Bounds.Top);
int right = (int)MathF.Ceiling(shape.Bounds.Right);
int bottom = (int)MathF.Ceiling(shape.Bounds.Bottom);
this.Bounds = Rectangle.FromLTRB(left, top, right, bottom);
}
/// <summary>
@ -42,24 +46,20 @@ namespace ImageSharp.Drawing
/// <inheritdoc/>
public override int Scan(float y, Span<float> buffer)
{
Vector2 start = new Vector2(this.Bounds.Left - 1, y);
Vector2 end = new Vector2(this.Bounds.Right + 1, y);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(buffer.Length);
try
var start = new PointF(this.Bounds.Left - 1, y);
var end = new PointF(this.Bounds.Right + 1, y);
using (var innerBuffer = new Buffer<PointF>(buffer.Length))
{
int count = this.Shape.FindIntersections(start, end, innerbuffer, buffer.Length, 0);
var span = innerBuffer.Span;
int count = this.Shape.FindIntersections(start, end, span);
for (int i = 0; i < count; i++)
{
buffer[i] = innerbuffer[i].X;
buffer[i] = span[i].X;
}
return count;
}
finally
{
ArrayPool<Vector2>.Shared.Return(innerbuffer);
}
}
}
}
}

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

@ -12,6 +12,7 @@ namespace ImageSharp.Drawing.Processors
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using ImageSharp.Processing;
using SixLabors.Primitives;
/// <summary>
/// Combines two images together by blending the pixels.

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

@ -14,6 +14,7 @@ namespace ImageSharp.Drawing.Processors
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using ImageSharp.Processing;
using SixLabors.Primitives;
/// <summary>
/// Using the bursh as a source of pixels colors blends the brush color with source.

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

@ -13,6 +13,7 @@ namespace ImageSharp.Drawing.Processors
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using ImageSharp.Processing;
using SixLabors.Primitives;
/// <summary>
/// Usinf a brsuh and a shape fills shape with contents of brush the

1
src/ImageSharp.Drawing/Region.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Drawing
{
using System;
using SixLabors.Primitives;
/// <summary>
/// Represents a region of an image.

8
src/ImageSharp.Drawing/Text/DrawText.Path.cs

@ -166,13 +166,15 @@ namespace ImageSharp
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, IPath path, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
Vector2 dpi = DefaultTextDpi;
float dpiX = DefaultTextDpi;
float dpiY = DefaultTextDpi;
if (options.UseImageResolution)
{
dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
dpiX = (float)source.MetaData.HorizontalResolution;
dpiY = (float)source.MetaData.VerticalResolution;
}
var style = new FontSpan(font, dpi)
var style = new RendererOptions(font, dpiX, dpiY)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,

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

@ -12,6 +12,7 @@ namespace ImageSharp
using Drawing.Pens;
using ImageSharp.PixelFormats;
using SixLabors.Fonts;
using SixLabors.Primitives;
using SixLabors.Shapes;
/// <summary>
@ -19,7 +20,7 @@ namespace ImageSharp
/// </summary>
public static partial class ImageExtensions
{
private static readonly Vector2 DefaultTextDpi = new Vector2(72);
private static readonly int DefaultTextDpi = 72;
/// <summary>
/// Draws the text onto the the image filled via the brush.
@ -33,7 +34,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, TPixel color, Vector2 location)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, TPixel color, PointF location)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, color, location, TextGraphicsOptions.Default);
@ -52,7 +53,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, TPixel color, Vector2 location, TextGraphicsOptions options)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, TPixel color, PointF location, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, Brushes.Solid(color), null, location, options);
@ -70,7 +71,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, Vector2 location)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, PointF location)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default);
@ -89,7 +90,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, Vector2 location, TextGraphicsOptions options)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, PointF location, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, brush, null, location, options);
@ -107,7 +108,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IPen<TPixel> pen, Vector2 location)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default);
@ -126,7 +127,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IPen<TPixel> pen, Vector2 location, TextGraphicsOptions options)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IPen<TPixel> pen, PointF location, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, null, pen, location, options);
@ -145,7 +146,7 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, Vector2 location)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
where TPixel : struct, IPixel<TPixel>
{
return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default);
@ -165,16 +166,18 @@ namespace ImageSharp
/// <returns>
/// The <see cref="Image{TPixel}" />.
/// </returns>
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, Vector2 location, TextGraphicsOptions options)
public static Image<TPixel> DrawText<TPixel>(this Image<TPixel> source, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location, TextGraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
{
Vector2 dpi = DefaultTextDpi;
float dpiX = DefaultTextDpi;
float dpiY = DefaultTextDpi;
if (options.UseImageResolution)
{
dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
dpiX = (float)source.MetaData.HorizontalResolution;
dpiY = (float)source.MetaData.VerticalResolution;
}
var style = new FontSpan(font, dpi)
var style = new RendererOptions(font, dpiX, dpiY, location)
{
ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth,
@ -183,7 +186,7 @@ namespace ImageSharp
VerticalAlignment = options.VerticalAlignment
};
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, location, style);
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, style);
var pathOptions = (GraphicsOptions)options;
if (brush != null)

2
src/ImageSharp/Common/Exceptions/ImageFormatException.cs

@ -11,7 +11,7 @@ namespace ImageSharp
/// The exception that is thrown when the library tries to load
/// an image, which has an invalid format.
/// </summary>
public class ImageFormatException : Exception
public sealed class ImageFormatException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class.

2
src/ImageSharp/Common/Exceptions/ImageProcessingException.cs

@ -10,7 +10,7 @@ namespace ImageSharp
/// <summary>
/// The exception that is thrown when an error occurs when applying a process to an image.
/// </summary>
public class ImageProcessingException : Exception
public sealed class ImageProcessingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.

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

@ -11,6 +11,7 @@ namespace ImageSharp
using System.Runtime.CompilerServices;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary>
/// Provides common mathematical methods.

244
src/ImageSharp/Configuration.cs

@ -6,8 +6,8 @@
namespace ImageSharp
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
@ -17,22 +17,32 @@ namespace ImageSharp
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public class Configuration
public sealed class Configuration
{
/// <summary>
/// A lazily initialized configuration default instance.
/// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(() => CreateDefaultInstance());
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
/// <summary>
/// An object that can be used to synchronize access to the <see cref="Configuration"/>.
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly object syncRoot = new object();
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>.
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly List<IImageFormat> imageFormatsList = new List<IImageFormat>();
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private readonly List<IImageFormatDetector> imageFormatDetectors = new List<IImageFormatDetector>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>();
/// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class.
@ -44,12 +54,15 @@ namespace ImageSharp
/// <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)
/// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IConfigurationModule[] configurationModules)
{
foreach (IImageFormat p in providers)
if (configurationModules != null)
{
this.AddImageFormat(p);
foreach (IConfigurationModule p in configurationModules)
{
p.Configure(this);
}
}
}
@ -58,21 +71,36 @@ namespace ImageSharp
/// </summary>
public static Configuration Default { get; } = Lazy.Value;
/// <summary>
/// Gets the collection of supported <see cref="IImageFormat"/>
/// </summary>
public IReadOnlyCollection<IImageFormat> ImageFormats => new ReadOnlyCollection<IImageFormat>(this.imageFormatsList);
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Gets the maximum header size of all formats.
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
internal IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
#if !NETSTANDARD1_1
/// <summary>
/// Gets or sets the fielsystem helper for accessing the local file system.
@ -81,127 +109,147 @@ namespace ImageSharp
#endif
/// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats.
/// Registers a new format provider.
/// </summary>
/// <param name="format">The new format to add.</param>
/// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration)
{
Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this);
}
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a well know format.</param>
public void AddImageFormat(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
Guard.NotNull(format.Encoder, nameof(format), "The encoder should not be null.");
Guard.NotNull(format.Decoder, nameof(format), "The decoder should not be null.");
Guard.NotNullOrEmpty(format.MimeType, nameof(format), "The mime type should not be null or empty.");
Guard.NotNullOrEmpty(format.Extension, nameof(format), "The extension should not be null or empty.");
Guard.NotNullOrEmpty(format.SupportedExtensions, nameof(format), "The supported extensions not be null or empty.");
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
this.imageFormats.Add(format);
}
this.AddImageFormatLocked(format);
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtensions(string extension)
{
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Creates the default instance, with Png, Jpeg, Gif and Bmp preregisterd (if they have been referenced)
/// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/> </returns>
internal static Configuration CreateDefaultInstance()
/// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType)
{
Configuration config = new Configuration();
// lets try auto loading the known image formats
config.AddImageFormat(new Formats.PngFormat());
config.AddImageFormat(new Formats.JpegFormat());
config.AddImageFormat(new Formats.GifFormat());
config.AddImageFormat(new Formats.BmpFormat());
config.AddImageFormat(new Formats.TiffFormat());
return config;
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Tries the add image format.
/// Sets a specific image encoder as the encoder for a specific image format.
/// </summary>
/// <param name="typeName">Name of the type.</param>
/// <returns>True if type discoverd and is a valid <see cref="IImageFormat"/></returns>
internal bool TryAddImageFormat(string typeName)
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{
Type type = Type.GetType(typeName, false);
if (type != null)
{
IImageFormat format = Activator.CreateInstance(type) as IImageFormat;
if (format != null
&& format.Encoder != null
&& format.Decoder != null
&& !string.IsNullOrEmpty(format.MimeType)
&& format.SupportedExtensions?.Any() == true)
{
// we can use the locked version as we have already validated in the if.
this.AddImageFormatLocked(format);
return true;
}
}
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
}
return false;
/// <summary>
/// Sets a specific image decoder as the decoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Adds image format. The class is locked to make it thread safe.
/// Removes all the registered image format detectors.
/// </summary>
/// <param name="format">The image format.</param>
private void AddImageFormatLocked(IImageFormat format)
public void ClearImageFormatDetectors()
{
lock (this.syncRoot)
{
if (this.GuardDuplicate(format))
{
this.imageFormatsList.Add(format);
this.imageFormatDetectors.Clear();
}
this.SetMaxHeaderSize();
}
}
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector)
{
Guard.NotNull(detector, nameof(detector));
this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// Checks to ensure duplicate image formats are not added.
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// <para><see cref="PngConfigurationModule"/></para>
/// <para><see cref="JpegConfigurationModule"/></para>
/// <para><see cref="GifConfigurationModule"/></para>
/// <para><see cref="BmpConfigurationModule"/></para>
/// </summary>
/// <param name="format">The image format.</param>
/// <exception cref="ArgumentException">Thrown if a duplicate is added.</exception>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
private bool GuardDuplicate(IImageFormat format)
/// <returns>The default configuration of <see cref="Configuration"/></returns>
internal static Configuration CreateDefaultInstance()
{
if (!format.SupportedExtensions.Contains(format.Extension, StringComparer.OrdinalIgnoreCase))
{
throw new ArgumentException("The supported extensions should contain the default extension.", nameof(format));
}
return new Configuration(
new PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
}
// ReSharper disable once ConvertClosureToMethodGroup
// Prevents method group allocation
if (format.SupportedExtensions.Any(e => string.IsNullOrWhiteSpace(e)))
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
internal IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
{
throw new ArgumentException("The supported extensions should not contain empty values.", nameof(format));
return decoder;
}
// If there is already a format with the same extension or a format that supports that
// extension return false.
foreach (IImageFormat imageFormat in this.imageFormatsList)
{
if (imageFormat.Extension.Equals(format.Extension, StringComparison.OrdinalIgnoreCase))
{
return false;
}
return null;
}
if (imageFormat.SupportedExtensions.Intersect(format.SupportedExtensions, StringComparer.OrdinalIgnoreCase).Any())
{
return false;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
internal IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
{
return encoder;
}
return true;
return null;
}
/// <summary>
/// Sets max header size.
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.imageFormatsList.Max(x => x.HeaderSize);
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}

21
src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs

@ -0,0 +1,21 @@
// <copyright file="BmpConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
public sealed class BmpConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Bitmap, new BmpEncoder());
config.SetDecoder(ImageFormats.Bitmap, new BmpDecoder());
config.AddImageFormatDetector(new BmpImageFormatDetector());
}
}
}

25
src/ImageSharp/Formats/Bmp/BmpConstants.cs

@ -0,0 +1,25 @@
// <copyright file="BmpConstants.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Collections.Generic;
/// <summary>
/// Defines constants relating to BMPs
/// </summary>
internal static class BmpConstants
{
/// <summary>
/// The list of mimetypes that equate to a bmp.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/bmp", "image/x-windows-bmp" };
/// <summary>
/// The list of file extensions that equate to a bmp.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "bm", "bmp", "dip" };
}
}

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

@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
@ -25,16 +26,16 @@ namespace ImageSharp.Formats
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
/// </remarks>
public class BmpDecoder : IImageDecoder
public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options)
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(stream, "stream");
return new BmpDecoderCore(configuration).Decode<TPixel>(stream);
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
}
}
}

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

@ -52,7 +52,8 @@ namespace ImageSharp.Formats
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public BmpDecoderCore(Configuration configuration)
/// <param name="options">The options</param>
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
}

25
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
@ -14,28 +15,18 @@ namespace ImageSharp.Formats
/// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary>
/// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
public class BmpEncoder : IImageEncoder
public sealed class BmpEncoder : IImageEncoder, IBmpEncoderOptions
{
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
IBmpEncoderOptions bmpOptions = BmpEncoderOptions.Create(options);
this.Encode(image, stream, bmpOptions);
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// Gets or sets the number of bits per pixel.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="options">The options for the encoder.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IBmpEncoderOptions options)
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
BmpEncoderCore encoder = new BmpEncoderCore(options);
var encoder = new BmpEncoderCore(this);
encoder.Encode(image, stream);
}
}

18
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -18,22 +18,22 @@ namespace ImageSharp.Formats
internal sealed class BmpEncoderCore
{
/// <summary>
/// The options for the encoder.
/// The amount to pad each row by.
/// </summary>
private readonly IBmpEncoderOptions options;
private int padding;
/// <summary>
/// The amount to pad each row by.
/// Gets or sets the number of bits per pixel.
/// </summary>
private int padding;
private BmpBitsPerPixel bitsPerPixel;
/// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <param name="options">The encoder options</param>
public BmpEncoderCore(IBmpEncoderOptions options)
{
this.options = options ?? new BmpEncoderOptions();
this.bitsPerPixel = options.BitsPerPixel;
}
/// <summary>
@ -49,9 +49,9 @@ namespace ImageSharp.Formats
Guard.NotNull(stream, nameof(stream));
// Cast to int will get the bytes per pixel
short bpp = (short)(8 * (int)this.options.BitsPerPixel);
short bpp = (short)(8 * (int)this.bitsPerPixel);
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
this.padding = bytesPerLine - (image.Width * (int)this.options.BitsPerPixel);
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
@ -136,7 +136,7 @@ namespace ImageSharp.Formats
{
using (PixelAccessor<TPixel> pixels = image.Lock())
{
switch (this.options.BitsPerPixel)
switch (this.bitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(writer, pixels);

45
src/ImageSharp/Formats/Bmp/BmpEncoderOptions.cs

@ -1,45 +0,0 @@
// <copyright file="BmpEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Encapsulates the options for the <see cref="BmpEncoder"/>.
/// </summary>
public sealed class BmpEncoderOptions : EncoderOptions, IBmpEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderOptions"/> class.
/// </summary>
public BmpEncoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
private BmpEncoderOptions(IEncoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
/// <summary>
/// Converts the options to a <see cref="IBmpEncoderOptions"/> instance with a cast
/// or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <returns>The options for the <see cref="BmpEncoder"/>.</returns>
internal static IBmpEncoderOptions Create(IEncoderOptions options)
{
return options as IBmpEncoderOptions ?? new BmpEncoderOptions(options);
}
}
}

2
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -15,7 +15,7 @@ namespace ImageSharp.Formats
/// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first).
/// </remarks>
internal class BmpFileHeader
internal sealed class BmpFileHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.

30
src/ImageSharp/Formats/Bmp/BmpFormat.cs

@ -1,4 +1,4 @@
// <copyright file="BmpFormat.cs" company="James Jackson-South">
// <copyright file="GifFormat.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -8,34 +8,20 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <summary>
/// Encapsulates the means to encode and decode bitmap images.
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public class BmpFormat : IImageFormat
internal sealed class BmpFormat : IImageFormat
{
/// <inheritdoc/>
public string MimeType => "image/bmp";
public string Name => "BMP";
/// <inheritdoc/>
public string Extension => "bmp";
public string DefaultMimeType => "image/bmp";
/// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "bmp", "dip" };
public IEnumerable<string> MimeTypes => BmpConstants.MimeTypes;
/// <inheritdoc/>
public IImageDecoder Decoder => new BmpDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new BmpEncoder();
/// <inheritdoc/>
public int HeaderSize => 2;
/// <inheritdoc/>
public bool IsSupportedFileFormat(byte[] header)
{
return header.Length >= this.HeaderSize &&
header[0] == 0x42 && // B
header[1] == 0x4D; // M
}
public IEnumerable<string> FileExtensions => BmpConstants.FileExtensions;
}
}
}

37
src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs

@ -0,0 +1,37 @@
// <copyright file="BmpImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
/// <summary>
/// Detects bmp file headers
/// </summary>
public sealed class BmpImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 2;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Bitmap;
}
return null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
return header.Length >= this.HeaderSize &&
header[0] == 0x42 && // B
header[1] == 0x4D; // M
}
}
}

2
src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs

@ -10,7 +10,7 @@ namespace ImageSharp.Formats
/// the screen.
/// <see href="https://en.wikipedia.org/wiki/BMP_file_format"/>
/// </summary>
internal class BmpInfoHeader
internal sealed class BmpInfoHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.

21
src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs

@ -0,0 +1,21 @@
// <copyright file="BmpDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary>
/// Image decoder options for decoding Windows bitmap streams.
/// </summary>
internal interface IBmpDecoderOptions
{
// added this for consistancy so we can add stuff as required, no options currently availible
}
}

13
src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs

@ -1,14 +1,21 @@
// <copyright file="IBmpEncoderOptions.cs" company="James Jackson-South">
// <copyright file="BmpEncoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary>
/// Encapsulates the options for the <see cref="BmpEncoder"/>.
/// Configuration options for use during bmp encoding
/// </summary>
public interface IBmpEncoderOptions : IEncoderOptions
/// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
internal interface IBmpEncoderOptions
{
/// <summary>
/// Gets the number of bits per pixel.

37
src/ImageSharp/Formats/DecoderOptions.cs

@ -1,37 +0,0 @@
// <copyright file="DecoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Encapsulates the shared decoder options.
/// </summary>
public class DecoderOptions : IDecoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="DecoderOptions"/> class.
/// </summary>
public DecoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DecoderOptions"/> class.
/// </summary>
/// <param name="options">The decoder options</param>
protected DecoderOptions(IDecoderOptions options)
{
if (options != null)
{
this.IgnoreMetadata = options.IgnoreMetadata;
}
}
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
}
}

37
src/ImageSharp/Formats/EncoderOptions.cs

@ -1,37 +0,0 @@
// <copyright file="EncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Encapsulates the shared encoder options.
/// </summary>
public class EncoderOptions : IEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="EncoderOptions"/> class.
/// </summary>
public EncoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EncoderOptions"/> class.
/// </summary>
/// <param name="options">The encoder options</param>
protected EncoderOptions(IEncoderOptions options)
{
if (options != null)
{
this.IgnoreMetadata = options.IgnoreMetadata;
}
}
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
}
}

22
src/ImageSharp/Formats/Gif/GifConfigurationModule.cs

@ -0,0 +1,22 @@
// <copyright file="GifConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary>
public sealed class GifConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Gif, new GifEncoder());
config.SetDecoder(ImageFormats.Gif, new GifDecoder());
config.AddImageFormatDetector(new GifImageFormatDetector());
}
}
}

15
src/ImageSharp/Formats/Gif/GifConstants.cs

@ -5,6 +5,7 @@
namespace ImageSharp.Formats
{
using System.Collections.Generic;
using System.Text;
/// <summary>
@ -90,6 +91,16 @@ namespace ImageSharp.Formats
/// <summary>
/// Gets the default encoding to use when reading comments.
/// </summary>
public static Encoding DefaultEncoding { get; } = Encoding.GetEncoding("ASCII");
public static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII");
/// <summary>
/// The list of mimetypes that equate to a gif.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/gif" };
/// <summary>
/// The list of file extensions that equate to a gif.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "gif" };
}
}
}

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

@ -6,37 +6,32 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Decoder for generating an image out of a gif encoded stream.
/// </summary>
public class GifDecoder : IImageDecoder
public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options);
return this.Decode<TPixel>(configuration, stream, gifOptions);
}
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TPixel}"/>.
/// Gets or sets the encoding that should be used when reading comments.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
/// <returns>The image thats been decoded.</returns>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IGifDecoderOptions options)
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
return new GifDecoderCore<TPixel>(options, configuration).Decode(stream);
var decoder = new GifDecoderCore<TPixel>(configuration, this);
return decoder.Decode(stream);
}
}
}

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

@ -12,12 +12,13 @@ namespace ImageSharp.Formats
using System.Text;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary>
/// Performs the gif decoding operation.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GifDecoderCore<TPixel>
internal sealed class GifDecoderCore<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -25,11 +26,6 @@ namespace ImageSharp.Formats
/// </summary>
private readonly byte[] buffer = new byte[16];
/// <summary>
/// The decoder options.
/// </summary>
private readonly IGifDecoderOptions options;
/// <summary>
/// The global configuration.
/// </summary>
@ -83,14 +79,25 @@ namespace ImageSharp.Formats
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TPixel}"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="configuration">The configuration.</param>
public GifDecoderCore(IGifDecoderOptions options, Configuration configuration)
/// <param name="options">The decoder options.</param>
public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
{
this.options = options ?? new GifDecoderOptions();
this.TextEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.IgnoreMetadata = options.IgnoreMetadata;
this.configuration = configuration ?? Configuration.Default;
}
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; internal set; }
/// <summary>
/// Gets the text encoding
/// </summary>
public Encoding TextEncoding { get; private set; }
/// <summary>
/// Decodes the stream to the image.
/// </summary>
@ -268,7 +275,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentLength}'");
}
if (this.options.IgnoreMetadata)
if (this.IgnoreMetadata)
{
this.currentStream.Seek(length, SeekOrigin.Current);
continue;
@ -279,7 +286,7 @@ namespace ImageSharp.Formats
try
{
this.currentStream.Read(commentsBuffer, 0, length);
string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer, 0, length);
this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
finally
@ -363,8 +370,6 @@ namespace ImageSharp.Formats
if (this.previousFrame == null)
{
this.metaData.Quality = colorTableLength / 3;
// This initializes the image to become fully transparent because the alpha channel is zero.
this.image = new Image<TPixel>(this.configuration, imageWidth, imageHeight, this.metaData);

47
src/ImageSharp/Formats/Gif/GifDecoderOptions.cs

@ -1,47 +0,0 @@
// <copyright file="GifDecoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Text;
/// <summary>
/// Encapsulates the options for the <see cref="GifDecoder"/>.
/// </summary>
public sealed class GifDecoderOptions : DecoderOptions, IGifDecoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderOptions"/> class.
/// </summary>
public GifDecoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GifDecoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the decoder.</param>
private GifDecoderOptions(IDecoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the encoding that should be used when reading comments.
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Converts the options to a <see cref="IGifDecoderOptions"/> instance with a cast
/// or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the decoder.</param>
/// <returns>The options for the <see cref="GifDecoder"/>.</returns>
internal static IGifDecoderOptions Create(IDecoderOptions options)
{
return options as IGifDecoderOptions ?? new GifDecoderOptions(options);
}
}
}

44
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -6,35 +6,47 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary>
/// Image encoder for writing image data to a stream in gif format.
/// </summary>
public class GifEncoder : IImageEncoder
public sealed class GifEncoder : IImageEncoder, IGifEncoderOptions
{
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
IGifEncoderOptions gifOptions = GifEncoderOptions.Create(options);
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
this.Encode(image, stream, gifOptions);
}
/// <summary>
/// Gets or sets the encoding that should be used when writing comments.
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// Gets or sets the size of the color palette to use. For gifs the value ranges from 1 to 256. Leave as zero for default size.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="options">The options for the encoder.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IGifEncoderOptions options)
public int PaletteSize { get; set; } = 0;
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 128;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
GifEncoderCore encoder = new GifEncoderCore(options);
GifEncoderCore encoder = new GifEncoderCore(this);
encoder.Encode(image, stream);
}
}

66
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -9,7 +9,7 @@ namespace ImageSharp.Formats
using System.Buffers;
using System.IO;
using System.Linq;
using System.Text;
using ImageSharp.PixelFormats;
using IO;
@ -25,11 +25,6 @@ namespace ImageSharp.Formats
/// </summary>
private readonly byte[] buffer = new byte[16];
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IGifEncoderOptions options;
/// <summary>
/// The number of bits requires to store the image palette.
/// </summary>
@ -40,19 +35,44 @@ namespace ImageSharp.Formats
/// </summary>
private bool hasFrames;
/// <summary>
/// Gets the TextEncoding
/// </summary>
private Encoding textEncoding;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
private IQuantizer quantizer;
/// <summary>
/// Gets or sets the threshold.
/// </summary>
private byte threshold;
/// <summary>
/// Gets or sets the size of the color palette to use.
/// </summary>
private int paletteSize;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private bool ignoreMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
public GifEncoderCore(IGifEncoderOptions options)
{
this.options = options ?? new GifEncoderOptions();
}
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
this.quantizer = options.Quantizer;
this.threshold = options.Threshold;
this.paletteSize = options.PaletteSize;
this.ignoreMetadata = options.IgnoreMetadata;
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
@ -66,26 +86,26 @@ namespace ImageSharp.Formats
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.Quantizer = this.options.Quantizer ?? new OctreeQuantizer<TPixel>();
this.quantizer = this.quantizer ?? new OctreeQuantizer<TPixel>();
// Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
// Ensure that quality can be set but has a fallback.
int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality;
quality = quality > 0 ? quality.Clamp(1, 256) : 256;
// Ensure that pallete size can be set but has a fallback.
int paletteSize = this.paletteSize;
paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256;
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quality);
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize);
// Quantize the image returning a palette.
this.hasFrames = image.Frames.Any();
// Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames.
var ditheredQuantizer = (IQuantizer<TPixel>)this.Quantizer;
var ditheredQuantizer = (IQuantizer<TPixel>)this.quantizer;
ditheredQuantizer.Dither = !this.hasFrames;
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image, quality);
// Quantize the image returning a palette.
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image, paletteSize);
int index = this.GetTransparentIndex(quantized);
@ -111,7 +131,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
QuantizedImage<TPixel> quantizedFrame = ditheredQuantizer.Quantize(frame, quality);
QuantizedImage<TPixel> quantizedFrame = ditheredQuantizer.Quantize(frame, paletteSize);
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame));
this.WriteImageDescriptor(frame, writer);
@ -240,7 +260,7 @@ namespace ImageSharp.Formats
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
if (this.options.IgnoreMetadata)
if (this.ignoreMetadata)
{
return;
}
@ -251,7 +271,7 @@ namespace ImageSharp.Formats
return;
}
byte[] comments = this.options.TextEncoding.GetBytes(property.Value);
byte[] comments = this.textEncoding.GetBytes(property.Value);
int count = Math.Min(comments.Length, 255);

65
src/ImageSharp/Formats/Gif/GifEncoderOptions.cs

@ -1,65 +0,0 @@
// <copyright file="GifEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Text;
using Quantizers;
/// <summary>
/// Encapsulates the options for the <see cref="GifEncoder"/>.
/// </summary>
public sealed class GifEncoderOptions : EncoderOptions, IGifEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderOptions"/> class.
/// </summary>
public GifEncoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
private GifEncoderOptions(IEncoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the encoding that should be used when writing comments.
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
/// <remarks>For gifs the value ranges from 1 to 256.</remarks>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 128;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Converts the options to a <see cref="IGifEncoderOptions"/> instance with a
/// cast or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <returns>The options for the <see cref="GifEncoder"/>.</returns>
internal static IGifEncoderOptions Create(IEncoderOptions options)
{
return options as IGifEncoderOptions ?? new GifEncoderOptions(options);
}
}
}

32
src/ImageSharp/Formats/Gif/GifFormat.cs

@ -8,38 +8,20 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <summary>
/// Encapsulates the means to encode and decode gif images.
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public class GifFormat : IImageFormat
internal sealed class GifFormat : IImageFormat
{
/// <inheritdoc/>
public string Extension => "gif";
public string Name => "GIF";
/// <inheritdoc/>
public string MimeType => "image/gif";
public string DefaultMimeType => "image/gif";
/// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "gif" };
public IEnumerable<string> MimeTypes => GifConstants.MimeTypes;
/// <inheritdoc/>
public IImageDecoder Decoder => new GifDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new GifEncoder();
/// <inheritdoc/>
public int HeaderSize => 6;
/// <inheritdoc/>
public bool IsSupportedFileFormat(byte[] header)
{
return header.Length >= this.HeaderSize &&
header[0] == 0x47 && // G
header[1] == 0x49 && // I
header[2] == 0x46 && // F
header[3] == 0x38 && // 8
(header[4] == 0x39 || header[4] == 0x37) && // 9 or 7
header[5] == 0x61; // a
}
public IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
}
}
}

41
src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs

@ -0,0 +1,41 @@
// <copyright file="GifImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
/// <summary>
/// Detects gif file headers
/// </summary>
public sealed class GifImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 6;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Gif;
}
return null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
return header.Length >= this.HeaderSize &&
header[0] == 0x47 && // G
header[1] == 0x49 && // I
header[2] == 0x46 && // F
header[3] == 0x38 && // 8
(header[4] == 0x39 || header[4] == 0x37) && // 9 or 7
header[5] == 0x61; // a
}
}
}

15
src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs

@ -1,17 +1,26 @@
// <copyright file="IGifDecoderOptions.cs" company="James Jackson-South">
// <copyright file="GifDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Encapsulates the options for the <see cref="GifDecoder"/>.
/// Decoder for generating an image out of a gif encoded stream.
/// </summary>
public interface IGifDecoderOptions : IDecoderOptions
internal interface IGifDecoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the encoding that should be used when reading comments.
/// </summary>

25
src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs

@ -1,29 +1,36 @@
// <copyright file="IGifEncoderOptions.cs" company="James Jackson-South">
// <copyright file="GifEncoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Quantizers;
using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary>
/// Encapsulates the options for the <see cref="GifEncoder"/>.
/// The configuration options used for encoding gifs
/// </summary>
public interface IGifEncoderOptions : IEncoderOptions
internal interface IGifEncoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the encoding that should be used when writing comments.
/// </summary>
Encoding TextEncoding { get; }
/// <summary>
/// Gets the quality of output for images.
/// Gets the size of the color palette to use. For gifs the value ranges from 1 to 256. Leave as zero for default size.
/// </summary>
/// <remarks>For gifs the value ranges from 1 to 256.</remarks>
int Quality { get; }
int PaletteSize { get; }
/// <summary>
/// Gets the transparency threshold.
@ -33,6 +40,6 @@ namespace ImageSharp.Formats
/// <summary>
/// Gets the quantizer for reducing the color count.
/// </summary>
IQuantizer Quantizer { get; }
IQuantizer Quantizer { get; }
}
}

8
src/ImageSharp/Formats/Gif/ImageExtensions.cs

@ -39,16 +39,16 @@ namespace ImageSharp
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="options">The options for the encoder.</param>
/// <param name="encoder">The options for the encoder.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>
/// The <see cref="Image{TPixel}"/>.
/// </returns>
public static Image<TPixel> SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, IGifEncoderOptions options)
public static Image<TPixel> SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, GifEncoder encoder)
where TPixel : struct, IPixel<TPixel>
{
GifEncoder encoder = new GifEncoder();
encoder.Encode(source, stream, options);
encoder = encoder ?? new GifEncoder();
encoder.Encode(source, stream);
return source;
}

18
src/ImageSharp/Formats/IEncoderOptions.cs

@ -1,18 +0,0 @@
// <copyright file="IEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Encapsulates the shared encoder options.
/// </summary>
public interface IEncoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
bool IgnoreMetadata { get; }
}
}

4
src/ImageSharp/Formats/IImageDecoder.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
@ -21,9 +22,8 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
/// <returns>The decoded image</returns>
Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options)
Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>;
}
}

4
src/ImageSharp/Formats/IImageEncoder.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
@ -21,8 +22,7 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="options">The options for the encoder.</param>
void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options)
void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>;
}
}

45
src/ImageSharp/Formats/IImageFormat.cs

@ -8,53 +8,28 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <summary>
/// Encapsulates a supported image format, providing means to encode and decode an image.
/// Individual formats implements in this interface must be registered in the <see cref="Configuration"/>
/// Describes an image format.
/// </summary>
public interface IImageFormat
{
/// <summary>
/// Gets the standard identifier used on the Internet to indicate the type of data that a file contains.
/// Gets the name that describes this image format.
/// </summary>
string MimeType { get; }
string Name { get; }
/// <summary>
/// Gets the default file extension for this format.
/// Gets the default mimetype that the image foramt uses
/// </summary>
string Extension { get; }
string DefaultMimeType { get; }
/// <summary>
/// Gets the supported file extensions for this format.
/// Gets all the mimetypes that have been used by this image foramt.
/// </summary>
/// <returns>
/// The supported file extension.
/// </returns>
IEnumerable<string> SupportedExtensions { get; }
IEnumerable<string> MimeTypes { get; }
/// <summary>
/// Gets the image encoder for encoding an image from a stream.
/// Gets the file extensions this image format commonly uses.
/// </summary>
IImageEncoder Encoder { get; }
/// <summary>
/// Gets the image decoder for decoding an image from a stream.
/// </summary>
IImageDecoder Decoder { get; }
/// <summary>
/// Gets the size of the header for this image type.
/// </summary>
/// <value>The size of the header.</value>
int HeaderSize { get; }
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="header">The <see cref="T:byte[]"/> containing the file header.</param>
/// <returns>
/// True if the decoder supports the file header; otherwise, false.
/// </returns>
bool IsSupportedFileFormat(byte[] header);
IEnumerable<string> FileExtensions { get; }
}
}
}

30
src/ImageSharp/Formats/IImageFormatDetector.cs

@ -0,0 +1,30 @@
// <copyright file="IImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Used for detecting mime types from a file header
/// </summary>
public interface IImageFormatDetector
{
/// <summary>
/// Gets the size of the header for this image type.
/// </summary>
/// <value>The size of the header.</value>
int HeaderSize { get; }
/// <summary>
/// Detect mimetype
/// </summary>
/// <param name="header">The <see cref="T:byte[]"/> containing the file header.</param>
/// <returns>returns the mime type of detected othersie returns null</returns>
IImageFormat DetectFormat(ReadOnlySpan<byte> header);
}
}

1
src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs

@ -8,6 +8,7 @@ namespace ImageSharp.Formats.Jpg
using System.Buffers;
using ImageSharp.Memory;
using SixLabors.Primitives;
/// <summary>
/// Represents an image made up of three color components (luminance, blue chroma, red chroma)

14
src/ImageSharp/Formats/IDecoderOptions.cs → src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs

@ -1,14 +1,20 @@
// <copyright file="IDecoderOptions.cs" company="James Jackson-South">
// <copyright file="JpegDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary>
/// Encapsulates the shared decoder options.
/// Image decoder for generating an image out of a jpg stream.
/// </summary>
public interface IDecoderOptions
internal interface IJpegDecoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.

17
src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs

@ -1,15 +1,26 @@
// <copyright file="IJpegEncoderOptions.cs" company="James Jackson-South">
// <copyright file="JpegEncoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary>
/// Encapsulates the options for the <see cref="JpegEncoder"/>.
/// Encoder for writing the data image to a stream in jpeg format.
/// </summary>
public interface IJpegEncoderOptions : IEncoderOptions
internal interface IJpegEncoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).

8
src/ImageSharp/Formats/Jpeg/ImageExtensions.cs

@ -39,16 +39,16 @@ namespace ImageSharp
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="options">The options for the encoder.</param>
/// <param name="encoder">The options for the encoder.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>
/// The <see cref="Image{TPixel}"/>.
/// </returns>
public static Image<TPixel> SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream, IJpegEncoderOptions options)
public static Image<TPixel> SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream, JpegEncoder encoder)
where TPixel : struct, IPixel<TPixel>
{
JpegEncoder encoder = new JpegEncoder();
encoder.Encode(source, stream, options);
encoder = encoder ?? new JpegEncoder();
encoder.Encode(source, stream);
return source;
}

22
src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs

@ -0,0 +1,22 @@
// <copyright file="JpegConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public sealed class JpegConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
{
config.SetEncoder(ImageFormats.Jpeg, new JpegEncoder());
config.SetDecoder(ImageFormats.Jpeg, new JpegDecoder());
config.AddImageFormatDetector(new JpegImageFormatDetector());
}
}
}

12
src/ImageSharp/Formats/Jpeg/JpegConstants.cs

@ -5,6 +5,8 @@
namespace ImageSharp.Formats
{
using System.Collections.Generic;
/// <summary>
/// Defines jpeg constants defined in the specification.
/// </summary>
@ -15,6 +17,16 @@ namespace ImageSharp.Formats
/// </summary>
public const ushort MaxLength = 65535;
/// <summary>
/// The list of mimetypes that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/jpeg", "image/pjpeg" };
/// <summary>
/// The list of file extensions that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "jpg", "jpeg", "jfif" };
/// <summary>
/// Represents high detail chroma horizontal subsampling.
/// </summary>

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

@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
@ -13,15 +14,20 @@ namespace ImageSharp.Formats
/// <summary>
/// Image decoder for generating an image out of a jpg stream.
/// </summary>
public class JpegDecoder : IImageDecoder
public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options)
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(stream, "stream");
using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration))
using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this))
{
return decoder.Decode<TPixel>(stream);
}

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

@ -18,7 +18,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Performs the jpeg decoding operation.
/// </summary>
internal unsafe class JpegDecoderCore : IDisposable
internal sealed unsafe class JpegDecoderCore : IDisposable
{
/// <summary>
/// The maximum number of color components
@ -45,11 +45,6 @@ namespace ImageSharp.Formats
/// </summary>
private static YCbCrToRgbTables yCbCrToRgbTables = YCbCrToRgbTables.Create();
/// <summary>
/// The decoder options.
/// </summary>
private readonly IDecoderOptions options;
/// <summary>
/// The global configuration
/// </summary>
@ -103,12 +98,12 @@ namespace ImageSharp.Formats
/// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="configuration">The configuration.</param>
public JpegDecoderCore(IDecoderOptions options, Configuration configuration)
/// <param name="options">The options.</param>
public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options)
{
this.IgnoreMetadata = options.IgnoreMetadata;
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new DecoderOptions();
this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
this.Temp = new byte[2 * Block8x8F.ScalarCount];
@ -190,6 +185,11 @@ namespace ImageSharp.Formats
/// </summary>
public int TotalMCUCount => this.MCUCountX * this.MCUCountY;
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; private set; }
/// <summary>
/// Decodes the image from the specified <see cref="Stream"/> and sets
/// the data to image.
@ -938,7 +938,7 @@ namespace ImageSharp.Formats
/// <param name="metadata">The image.</param>
private void ProcessApp1Marker(int remaining, ImageMetaData metadata)
{
if (remaining < 6 || this.options.IgnoreMetadata)
if (remaining < 6 || this.IgnoreMetadata)
{
this.InputProcessor.Skip(remaining);
return;
@ -968,7 +968,7 @@ namespace ImageSharp.Formats
{
// Length is 14 though we only need to check 12.
const int Icclength = 14;
if (remaining < Icclength || this.options.IgnoreMetadata)
if (remaining < Icclength || this.IgnoreMetadata)
{
this.InputProcessor.Skip(remaining);
return;

36
src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

@ -5,6 +5,8 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
@ -12,16 +14,25 @@ namespace ImageSharp.Formats
/// <summary>
/// Encoder for writing the data image to a stream in jpeg format.
/// </summary>
public class JpegEncoder : IImageEncoder
public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions
{
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
IJpegEncoderOptions gifOptions = JpegEncoderOptions.Create(options);
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
this.Encode(image, stream, gifOptions);
}
/// <summary>
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// </summary>
/// <value>The quality of the jpg image from 0 to 100.</value>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the subsample ration, that will be used to encode the image.
/// </summary>
/// <value>The subsample ratio of the jpg image.</value>
public JpegSubsample? Subsample { get; set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
@ -29,12 +40,11 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="options">The options for the encoder.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IJpegEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
JpegEncoderCore encode = new JpegEncoderCore(options);
encode.Encode(image, stream);
var encoder = new JpegEncoderCore(this);
encoder.Encode(image, stream);
}
}
}

52
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -17,7 +17,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Image encoder for writing an image to a stream as a jpeg.
/// </summary>
internal unsafe class JpegEncoderCore
internal sealed unsafe class JpegEncoderCore
{
/// <summary>
/// The number of quantization tables.
@ -124,11 +124,6 @@ namespace ImageSharp.Formats
/// </summary>
private readonly byte[] huffmanBuffer = new byte[179];
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IJpegEncoderOptions options;
/// <summary>
/// The accumulated bits to write to the stream.
/// </summary>
@ -155,17 +150,38 @@ namespace ImageSharp.Formats
private Stream outputStream;
/// <summary>
/// The subsampling method to use.
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private bool ignoreMetadata = false;
/// <summary>
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// </summary>
private JpegSubsample subsample;
/// <value>The quality of the jpg image from 0 to 100.</value>
private int quality = 0;
/// <summary>
/// Gets or sets the subsampling method to use.
/// </summary>
private JpegSubsample? subsample;
/// <summary>
/// Initializes a new instance of the <see cref="JpegEncoderCore"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <param name="options">The options</param>
public JpegEncoderCore(IJpegEncoderOptions options)
{
this.options = options ?? new JpegEncoderOptions();
int quality = options.Quality;
if (quality == 0)
{
quality = 75;
}
this.quality = quality;
this.subsample = options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
this.ignoreMetadata = options.IgnoreMetadata;
}
/// <summary>
@ -186,21 +202,13 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}.");
}
// Ensure that quality can be set but has a fallback.
int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality;
if (quality == 0)
{
quality = 75;
}
quality = quality.Clamp(1, 100);
this.outputStream = stream;
this.subsample = this.options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
int quality = this.quality.Clamp(1, 100);
// Convert from a quality rating to a scaling factor.
int scale;
if (quality < 50)
if (this.quality < 50)
{
scale = 5000 / quality;
}
@ -788,7 +796,7 @@ namespace ImageSharp.Formats
private void WriteProfiles<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.options.IgnoreMetadata)
if (this.ignoreMetadata)
{
return;
}

56
src/ImageSharp/Formats/Jpeg/JpegEncoderOptions.cs

@ -1,56 +0,0 @@
// <copyright file="JpegEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Encapsulates the options for the <see cref="JpegEncoder"/>.
/// </summary>
public sealed class JpegEncoderOptions : EncoderOptions, IJpegEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="JpegEncoderOptions"/> class.
/// </summary>
public JpegEncoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JpegEncoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
private JpegEncoderOptions(IEncoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
/// </summary>
/// <remarks>
/// If the quality is less than or equal to 90, the subsampling ratio will switch to <see cref="JpegSubsample.Ratio420"/>
/// </remarks>
/// <value>The quality of the jpg image from 0 to 100.</value>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the subsample ration, that will be used to encode the image.
/// </summary>
/// <value>The subsample ratio of the jpg image.</value>
public JpegSubsample? Subsample { get; set; }
/// <summary>
/// Converts the options to a <see cref="IJpegEncoderOptions"/> instance with a
/// cast or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <returns>The options for the <see cref="JpegEncoder"/>.</returns>
internal static IJpegEncoderOptions Create(IEncoderOptions options)
{
return options as IJpegEncoderOptions ?? new JpegEncoderOptions(options);
}
}
}

76
src/ImageSharp/Formats/Jpeg/JpegFormat.cs

@ -8,82 +8,20 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <summary>
/// Encapsulates the means to encode and decode jpeg images.
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public class JpegFormat : IImageFormat
internal sealed class JpegFormat : IImageFormat
{
/// <inheritdoc/>
public string MimeType => "image/jpeg";
public string Name => "JPEG";
/// <inheritdoc/>
public string Extension => "jpg";
public string DefaultMimeType => "image/jpeg";
/// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "jpg", "jpeg", "jfif" };
public IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
/// <inheritdoc/>
public IImageDecoder Decoder => new JpegDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new JpegEncoder();
/// <inheritdoc/>
public int HeaderSize => 11;
/// <inheritdoc/>
public bool IsSupportedFileFormat(byte[] header)
{
return header.Length >= this.HeaderSize &&
(IsJfif(header) || IsExif(header) || IsJpeg(header));
}
/// <summary>
/// Returns a value indicating whether the given bytes identify Jfif data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsJfif(byte[] header)
{
bool isJfif =
header[6] == 0x4A && // J
header[7] == 0x46 && // F
header[8] == 0x49 && // I
header[9] == 0x46 && // F
header[10] == 0x00;
return isJfif;
}
/// <summary>
/// Returns a value indicating whether the given bytes identify EXIF data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsExif(byte[] header)
{
bool isExif =
header[6] == 0x45 && // E
header[7] == 0x78 && // X
header[8] == 0x69 && // I
header[9] == 0x66 && // F
header[10] == 0x00;
return isExif;
}
/// <summary>
/// Returns a value indicating whether the given bytes identify Jpeg data.
/// This is a last chance resort for jpegs that contain ICC information.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsJpeg(byte[] header)
{
bool isJpg =
header[0] == 0xFF && // 255
header[1] == 0xD8; // 216
return isJpg;
}
public IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
}
}
}

87
src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs

@ -0,0 +1,87 @@
// <copyright file="JpegImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
/// <summary>
/// Detects Jpeg file headers
/// </summary>
public sealed class JpegImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 11;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Jpeg;
}
return null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
return header.Length >= this.HeaderSize &&
(this.IsJfif(header) || this.IsExif(header) || this.IsJpeg(header));
}
/// <summary>
/// Returns a value indicating whether the given bytes identify Jfif data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJfif(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
bool isJfif =
header[6] == 0x4A && // J
header[7] == 0x46 && // F
header[8] == 0x49 && // I
header[9] == 0x46 && // F
header[10] == 0x00;
return isJfif;
}
/// <summary>
/// Returns a value indicating whether the given bytes identify EXIF data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsExif(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
bool isExif =
header[6] == 0x45 && // E
header[7] == 0x78 && // X
header[8] == 0x69 && // I
header[9] == 0x66 && // F
header[10] == 0x00;
return isExif;
}
/// <summary>
/// Returns a value indicating whether the given bytes identify Jpeg data.
/// This is a last chance resort for jpegs that contain ICC information.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJpeg(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
bool isJpg =
header[0] == 0xFF && // 255
header[1] == 0xD8; // 216
return isJpg;
}
}
}

15
src/ImageSharp/Formats/Png/IPngDecoderOptions.cs

@ -1,17 +1,26 @@
// <copyright file="IPngDecoderOptions.cs" company="James Jackson-South">
// <copyright file="PngDecoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
/// Encapsulates the options for the <see cref="PngDecoder"/>.
/// The optioas for decoding png images
/// </summary>
public interface IPngDecoderOptions : IDecoderOptions
internal interface IPngDecoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the encoding that should be used when reading text chunks.
/// </summary>

28
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -1,21 +1,30 @@
// <copyright file="IPngEncoderOptions.cs" company="James Jackson-South">
// <copyright file="PngEncoder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using Quantizers;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary>
/// Encapsulates the options for the <see cref="PngEncoder"/>.
/// The options availible for manipulating the encoder pipeline
/// </summary>
public interface IPngEncoderOptions : IEncoderOptions
internal interface IPngEncoderOptions
{
/// <summary>
/// Gets the quality of output for images.
/// Gets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the size of the color palette to use. Set to zero to leav png encoding to use pixel data.
/// </summary>
int Quality { get; }
int PaletteSize { get; }
/// <summary>
/// Gets the png color type
@ -24,19 +33,20 @@ namespace ImageSharp.Formats
/// <summary>
/// Gets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
int CompressionLevel { get; }
/// <summary>
/// Gets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true.
/// is set to true. The default value is 2.2F.
/// </summary>
/// <value>The gamma value of the image.</value>
float Gamma { get; }
/// <summary>
/// Gets quantizer for reducing the color count.
/// Gets quantizer for reducing the color count.
/// </summary>
IQuantizer Quantizer { get; }
@ -47,7 +57,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Gets a value indicating whether this instance should write
/// gamma information to the stream.
/// gamma information to the stream. The default value is false.
/// </summary>
bool WriteGamma { get; }
}

8
src/ImageSharp/Formats/Png/ImageExtensions.cs

@ -38,16 +38,16 @@ namespace ImageSharp
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="options">The options for the encoder.</param>
/// <param name="encoder">The options for the encoder.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>
/// The <see cref="Image{TPixel}"/>.
/// </returns>
public static Image<TPixel> SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream, IPngEncoderOptions options)
public static Image<TPixel> SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream, PngEncoder encoder)
where TPixel : struct, IPixel<TPixel>
{
PngEncoder encoder = new PngEncoder();
encoder.Encode(source, stream, options);
encoder = encoder ?? new PngEncoder();
encoder.Encode(source, stream);
return source;
}

21
src/ImageSharp/Formats/Png/PngConfigurationModule.cs

@ -0,0 +1,21 @@
// <copyright file="PngConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary>
public sealed class PngConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration host)
{
host.SetEncoder(ImageFormats.Png, new PngEncoder());
host.SetDecoder(ImageFormats.Png, new PngDecoder());
host.AddImageFormatDetector(new PngImageFormatDetector());
}
}
}

31
src/ImageSharp/Formats/Png/PngConstants.cs

@ -0,0 +1,31 @@
// <copyright file="PngConstants.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Collections.Generic;
using System.Text;
/// <summary>
/// Defines png constants defined in the specification.
/// </summary>
internal static class PngConstants
{
/// <summary>
/// The default encoding for text metadata.
/// </summary>
public static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII");
/// <summary>
/// The list of mimetypes that equate to a png.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/png" };
/// <summary>
/// The list of file extensions that equate to a png.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "png" };
}
}

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

@ -6,8 +6,9 @@
namespace ImageSharp.Formats
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ImageSharp.PixelFormats;
/// <summary>
@ -30,17 +31,17 @@ namespace ImageSharp.Formats
/// </list>
/// </para>
/// </remarks>
public class PngDecoder : IImageDecoder
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options);
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
return this.Decode<TPixel>(configuration, stream, pngOptions);
}
/// <summary>
/// Gets or sets the encoding that should be used when reading text chunks.
/// </summary>
public Encoding TextEncoding { get; set; } = PngConstants.DefaultEncoding;
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TPixel}"/>.
@ -48,12 +49,12 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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>
/// <returns>The decoded image.</returns>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IPngDecoderOptions options)
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
return new PngDecoderCore(options, configuration).Decode<TPixel>(stream);
var decoder = new PngDecoderCore(configuration, this);
return decoder.Decode<TPixel>(stream);
}
}
}

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

@ -11,7 +11,7 @@ namespace ImageSharp.Formats
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
@ -20,7 +20,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Performs the png decoding operation.
/// </summary>
internal class PngDecoderCore
internal sealed class PngDecoderCore
{
/// <summary>
/// The dictionary of available color types.
@ -28,10 +28,10 @@ namespace ImageSharp.Formats
private static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>()
{
[PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8 },
[PngColorType.Rgb] = new byte[] { 8 },
[PngColorType.Rgb] = new byte[] { 8, 16 },
[PngColorType.Palette] = new byte[] { 1, 2, 4, 8 },
[PngColorType.GrayscaleWithAlpha] = new byte[] { 8 },
[PngColorType.RgbWithAlpha] = new byte[] { 8 },
[PngColorType.RgbWithAlpha] = new byte[] { 8, 16 }
};
/// <summary>
@ -74,11 +74,6 @@ namespace ImageSharp.Formats
/// </summary>
private readonly char[] chars = new char[4];
/// <summary>
/// The decoder options.
/// </summary>
private readonly IPngDecoderOptions options;
/// <summary>
/// Reusable crc for validating chunks.
/// </summary>
@ -147,29 +142,40 @@ namespace ImageSharp.Formats
/// <summary>
/// The current pass for an interlaced PNG
/// </summary>
private int pass = 0;
private int pass;
/// <summary>
/// The current number of bytes read in the current scanline
/// </summary>
private int currentRowBytesRead = 0;
private int currentRowBytesRead;
/// <summary>
/// Gets or sets the png color type
/// </summary>
private PngColorType pngColorType;
/// <summary>
/// Gets the encoding to use
/// </summary>
private Encoding textEncoding;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private bool ignoreMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="configuration">The configuration.</param>
public PngDecoderCore(IPngDecoderOptions options, Configuration configuration)
/// <param name="options">The decoder options.</param>
public PngDecoderCore(Configuration configuration, IPngDecoderOptions options)
{
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new PngDecoderOptions();
this.textEncoding = options.TextEncoding ?? PngConstants.DefaultEncoding;
this.ignoreMetadata = options.IgnoreMetadata;
}
/// <summary>
/// Gets or sets the png color type
/// </summary>
public PngColorType PngColorType { get; set; }
/// <summary>
/// Decodes the stream to the image.
/// </summary>
@ -221,7 +227,6 @@ namespace ImageSharp.Formats
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length);
this.palette = pal;
metadata.Quality = pal.Length / 3;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
@ -325,11 +330,6 @@ namespace ImageSharp.Formats
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.header.Width > Image<TPixel>.MaxWidth || this.header.Height > Image<TPixel>.MaxHeight)
{
throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image<TPixel>.MaxWidth}x{Image<TPixel>.MaxHeight}'");
}
image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
@ -349,7 +349,7 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="int"/></returns>
private int CalculateBytesPerPixel()
{
switch (this.PngColorType)
switch (this.pngColorType)
{
case PngColorType.Grayscale:
return 1;
@ -361,10 +361,20 @@ namespace ImageSharp.Formats
return 1;
case PngColorType.Rgb:
if (this.header.BitDepth == 16)
{
return 6;
}
return 3;
// PngColorType.RgbWithAlpha:
case PngColorType.RgbWithAlpha:
default:
if (this.header.BitDepth == 16)
{
return 8;
}
return 4;
}
}
@ -378,15 +388,16 @@ namespace ImageSharp.Formats
/// </returns>
private int CalculateScanlineLength(int width)
{
int mod = this.header.BitDepth == 16 ? 16 : 8;
int scanlineLength = width * this.header.BitDepth * this.bytesPerPixel;
int amount = scanlineLength % 8;
int amount = scanlineLength % mod;
if (amount != 0)
{
scanlineLength += 8 - amount;
scanlineLength += mod - amount;
}
return scanlineLength / 8;
return scanlineLength / mod;
}
/// <summary>
@ -566,7 +577,7 @@ namespace ImageSharp.Formats
Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow);
var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.PngColorType)
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
@ -589,7 +600,7 @@ namespace ImageSharp.Formats
byte intensity = defilteredScanline[offset];
byte alpha = defilteredScanline[offset + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity));
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color;
}
@ -603,18 +614,59 @@ namespace ImageSharp.Formats
case PngColorType.Rgb:
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width);
if (this.header.BitDepth == 16)
{
int length = this.header.Width * 3;
using (var compressed = new Buffer<byte>(length))
{
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(scanlineBuffer, compressed, length);
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(compressed, rowSpan, this.header.Width);
}
}
else
{
PixelOperations<TPixel>.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width);
}
break;
case PngColorType.RgbWithAlpha:
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(scanlineBuffer, rowSpan, this.header.Width);
if (this.header.BitDepth == 16)
{
int length = this.header.Width * 4;
using (var compressed = new Buffer<byte>(length))
{
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(scanlineBuffer, compressed, length);
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(compressed, rowSpan, this.header.Width);
}
}
else
{
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(scanlineBuffer, rowSpan, this.header.Width);
}
break;
}
}
/// <summary>
/// Compresses the given span from 16bpp to 8bpp
/// </summary>
/// <param name="source">The source buffer</param>
/// <param name="target">The target buffer</param>
/// <param name="length">The target length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void From16BitTo8Bit(Span<byte> source, Span<byte> target, int length)
{
for (int i = 0, j = 0; i < length; i++, j += 2)
{
target[i] = (byte)((source[j + 1] << 8) + source[j]);
}
}
/// <summary>
/// Processes a scanline that uses a palette
/// </summary>
@ -625,10 +677,10 @@ namespace ImageSharp.Formats
where TPixel : struct, IPixel<TPixel>
{
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] palette = this.palette;
byte[] pal = this.palette;
var color = default(TPixel);
Rgba32 rgba = default(Rgba32);
var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
@ -643,7 +695,7 @@ namespace ImageSharp.Formats
if (rgba.A > 0)
{
rgba.Rgb = palette.GetRgb24(pixelOffset);
rgba.Rgb = pal.GetRgb24(pixelOffset);
}
else
{
@ -663,7 +715,7 @@ namespace ImageSharp.Formats
int index = newScanline[x + 1];
int pixelOffset = index * 3;
rgba.Rgb = palette.GetRgb24(pixelOffset);
rgba.Rgb = pal.GetRgb24(pixelOffset);
color.PackFromRgba32(rgba);
row[x] = color;
@ -684,11 +736,14 @@ namespace ImageSharp.Formats
{
var color = default(TPixel);
switch (this.PngColorType)
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] newScanline1 = ToArrayByBitsLength(
defilteredScanline,
this.bytesPerScanline,
this.header.BitDepth);
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++)
{
byte intensity = (byte)(newScanline1[o] * factor);
@ -712,8 +767,11 @@ namespace ImageSharp.Formats
case PngColorType.Palette:
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
Rgba32 rgba = default(Rgba32);
byte[] newScanline = ToArrayByBitsLength(
defilteredScanline,
this.bytesPerScanline,
this.header.BitDepth);
var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
@ -760,29 +818,75 @@ namespace ImageSharp.Formats
case PngColorType.Rgb:
rgba.A = 255;
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
if (this.header.BitDepth == 16)
{
rgba.R = defilteredScanline[o];
rgba.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
int length = this.header.Width * 3;
using (var compressed = new Buffer<byte>(length))
{
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(new Span<byte>(defilteredScanline), compressed, length);
for (int x = pixelOffset, o = 1;
x < this.header.Width;
x += increment, o += this.bytesPerPixel)
{
rgba.R = compressed[o];
rgba.G = compressed[o + this.bytesPerSample];
rgba.B = compressed[o + (2 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
}
}
else
{
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = defilteredScanline[o];
rgba.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
}
break;
case PngColorType.RgbWithAlpha:
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
if (this.header.BitDepth == 16)
{
rgba.R = defilteredScanline[o];
rgba.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)];
int length = this.header.Width * 4;
using (var compressed = new Buffer<byte>(length))
{
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(new Span<byte>(defilteredScanline), compressed, length);
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = compressed[o];
rgba.G = compressed[o + this.bytesPerSample];
rgba.B = compressed[o + (2 * this.bytesPerSample)];
rgba.A = compressed[o + (3 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
}
}
else
{
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
rgba.R = defilteredScanline[o];
rgba.G = defilteredScanline[o + this.bytesPerSample];
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)];
rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)];
color.PackFromRgba32(rgba);
rowSpan[x] = color;
}
}
break;
@ -797,7 +901,7 @@ namespace ImageSharp.Formats
/// <param name="length">The maximum length to read.</param>
private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length)
{
if (this.options.IgnoreMetadata)
if (this.ignoreMetadata)
{
return;
}
@ -813,8 +917,8 @@ namespace ImageSharp.Formats
}
}
string name = this.options.TextEncoding.GetString(data, 0, zeroIndex);
string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
string name = this.textEncoding.GetString(data, 0, zeroIndex);
string value = this.textEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
metadata.Properties.Add(new ImageProperty(name, value));
}
@ -868,7 +972,7 @@ namespace ImageSharp.Formats
throw new NotSupportedException("The png specification only defines 'None' and 'Adam7' as interlaced methods.");
}
this.PngColorType = (PngColorType)this.header.ColorType;
this.pngColorType = this.header.ColorType;
}
/// <summary>
@ -985,13 +1089,13 @@ namespace ImageSharp.Formats
/// <summary>
/// Returns the correct number of columns for each interlaced pass.
/// </summary>
/// <param name="pass">Th current pass index</param>
/// <param name="passIndex">Th current pass index</param>
/// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ComputeColumnsAdam7(int pass)
private int ComputeColumnsAdam7(int passIndex)
{
int width = this.header.Width;
switch (pass)
switch (passIndex)
{
case 0: return (width + 7) / 8;
case 1: return (width + 3) / 8;
@ -1000,7 +1104,7 @@ namespace ImageSharp.Formats
case 4: return (width + 1) / 2;
case 5: return width / 2;
case 6: return width;
default: throw new ArgumentException($"Not a valid pass index: {pass}");
default: throw new ArgumentException($"Not a valid pass index: {passIndex}");
}
}
}

49
src/ImageSharp/Formats/Png/PngDecoderOptions.cs

@ -1,49 +0,0 @@
// <copyright file="PngDecoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Text;
/// <summary>
/// Encapsulates the options for the <see cref="PngDecoder"/>.
/// </summary>
public sealed class PngDecoderOptions : DecoderOptions, IPngDecoderOptions
{
private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII");
/// <summary>
/// Initializes a new instance of the <see cref="PngDecoderOptions"/> class.
/// </summary>
public PngDecoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PngDecoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the decoder.</param>
private PngDecoderOptions(IDecoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the encoding that should be used when reading text chunks.
/// </summary>
public Encoding TextEncoding { get; set; } = DefaultEncoding;
/// <summary>
/// Converts the options to a <see cref="IPngDecoderOptions"/> instance with a cast
/// or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the decoder.</param>
/// <returns>The options for the <see cref="PngDecoder"/>.</returns>
internal static IPngDecoderOptions Create(IDecoderOptions options)
{
return options as IPngDecoderOptions ?? new PngDecoderOptions(options);
}
}
}

61
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -5,23 +5,61 @@
namespace ImageSharp.Formats
{
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary>
/// Image encoder for writing image data to a stream in png format.
/// </summary>
public class PngEncoder : IImageEncoder
public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions
{
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options)
where TPixel : struct, IPixel<TPixel>
{
IPngEncoderOptions pngOptions = PngEncoderOptions.Create(options);
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
this.Encode(image, stream, pngOptions);
}
/// <summary>
/// Gets or sets the size of the color palette to use. Set to zero to leav png encoding to use pixel data.
/// </summary>
public int PaletteSize { get; set; } = 0;
/// <summary>
/// Gets or sets the png color type
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
public int CompressionLevel { get; set; } = 6;
/// <summary>
/// Gets or sets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true. The default value is 2.2F.
/// </summary>
/// <value>The gamma value of the image.</value>
public float Gamma { get; set; } = 2.2F;
/// <summary>
/// Gets or sets quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 255;
/// <summary>
/// Gets or sets a value indicating whether this instance should write
/// gamma information to the stream. The default value is false.
/// </summary>
public bool WriteGamma { get; set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
@ -29,13 +67,12 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="options">The options for the encoder.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IPngEncoderOptions options)
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encode = new PngEncoderCore(options))
using (var encoder = new PngEncoderCore(this))
{
encode.Encode(image, stream);
encoder.Encode(image, stream);
}
}
}

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

@ -43,11 +43,6 @@ namespace ImageSharp.Formats
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IPngEncoderOptions options;
/// <summary>
/// Contains the raw pixel data from an indexed image.
/// </summary>
@ -113,11 +108,6 @@ namespace ImageSharp.Formats
/// </summary>
private Buffer<byte> paeth;
/// <summary>
/// The quality of output for images.
/// </summary>
private int quality;
/// <summary>
/// The png color type.
/// </summary>
@ -128,13 +118,50 @@ namespace ImageSharp.Formats
/// </summary>
private IQuantizer quantizer;
/// <summary>
/// Gets or sets a value indicating whether to ignore metadata
/// </summary>
private bool ignoreMetadata;
/// <summary>
/// Gets or sets the Quality value
/// </summary>
private int paletteSize;
/// <summary>
/// Gets or sets the CompressionLevel value
/// </summary>
private int compressionLevel;
/// <summary>
/// Gets or sets the Gamma value
/// </summary>
private float gamma;
/// <summary>
/// Gets or sets the Threshold value
/// </summary>
private byte threshold;
/// <summary>
/// Gets or sets a value indicating whether to Write Gamma
/// </summary>
private bool writeGamma;
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderCore"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <param name="options">The options for influancing the encoder</param>
public PngEncoderCore(IPngEncoderOptions options)
{
this.options = options ?? new PngEncoderOptions();
this.ignoreMetadata = options.IgnoreMetadata;
this.paletteSize = options.PaletteSize > 0 ? options.PaletteSize.Clamp(1, int.MaxValue) : int.MaxValue;
this.pngColorType = options.PngColorType;
this.compressionLevel = options.CompressionLevel;
this.gamma = options.Gamma;
this.quantizer = options.Quantizer;
this.threshold = options.Threshold;
this.writeGamma = options.WriteGamma;
}
/// <summary>
@ -164,27 +191,20 @@ namespace ImageSharp.Formats
stream.Write(this.chunkDataBuffer, 0, 8);
// Ensure that quality can be set but has a fallback.
this.quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality;
this.quality = this.quality > 0 ? this.quality.Clamp(1, int.MaxValue) : int.MaxValue;
this.pngColorType = this.options.PngColorType;
this.quantizer = this.options.Quantizer;
// Set correct color type if the color count is 256 or less.
if (this.quality <= 256)
if (this.paletteSize <= 256)
{
this.pngColorType = PngColorType.Palette;
}
if (this.pngColorType == PngColorType.Palette && this.quality > 256)
if (this.pngColorType == PngColorType.Palette && this.paletteSize > 256)
{
this.quality = 256;
this.paletteSize = 256;
}
// Set correct bit depth.
this.bitDepth = this.quality <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.quality).Clamp(1, 8)
this.bitDepth = this.paletteSize <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.paletteSize).Clamp(1, 8)
: (byte)8;
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
@ -514,7 +534,7 @@ namespace ImageSharp.Formats
private QuantizedImage<TPixel> WritePaletteChunk<TPixel>(Stream stream, PngHeader header, ImageBase<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.quality > 256)
if (this.paletteSize > 256)
{
return null;
}
@ -525,7 +545,7 @@ namespace ImageSharp.Formats
}
// Quantize the image returning a palette. This boxing is icky.
QuantizedImage<TPixel> quantized = ((IQuantizer<TPixel>)this.quantizer).Quantize(image, this.quality);
QuantizedImage<TPixel> quantized = ((IQuantizer<TPixel>)this.quantizer).Quantize(image, this.paletteSize);
// Grab the palette and write it to the stream.
TPixel[] palette = quantized.Palette;
@ -552,7 +572,7 @@ namespace ImageSharp.Formats
colorTable[offset + 1] = bytes[1];
colorTable[offset + 2] = bytes[2];
if (alpha > this.options.Threshold)
if (alpha > this.threshold)
{
alpha = 255;
}
@ -610,9 +630,9 @@ namespace ImageSharp.Formats
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
private void WriteGammaChunk(Stream stream)
{
if (this.options.WriteGamma)
if (this.writeGamma)
{
int gammaValue = (int)(this.options.Gamma * 100000F);
int gammaValue = (int)(this.gamma * 100000F);
byte[] size = BitConverter.GetBytes(gammaValue);
@ -655,7 +675,7 @@ namespace ImageSharp.Formats
try
{
memoryStream = new MemoryStream();
using (var deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel))
using (var deflateStream = new ZlibDeflateStream(memoryStream, this.compressionLevel))
{
for (int y = 0; y < this.height; y++)
{

82
src/ImageSharp/Formats/Png/PngEncoderOptions.cs

@ -1,82 +0,0 @@
// <copyright file="PngEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using Quantizers;
/// <summary>
/// Encapsulates the options for the <see cref="PngEncoder"/>.
/// </summary>
public sealed class PngEncoderOptions : EncoderOptions, IPngEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderOptions"/> class.
/// </summary>
public PngEncoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
private PngEncoderOptions(IEncoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the png color type
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
public int CompressionLevel { get; set; } = 6;
/// <summary>
/// Gets or sets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true. The default value is 2.2F.
/// </summary>
/// <value>The gamma value of the image.</value>
public float Gamma { get; set; } = 2.2F;
/// <summary>
/// Gets or sets quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 255;
/// <summary>
/// Gets or sets a value indicating whether this instance should write
/// gamma information to the stream. The default value is false.
/// </summary>
public bool WriteGamma { get; set; }
/// <summary>
/// Converts the options to a <see cref="IPngEncoderOptions"/> instance with a
/// cast or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <returns>The options for the <see cref="PngEncoder"/>.</returns>
internal static IPngEncoderOptions Create(IEncoderOptions options)
{
return options as IPngEncoderOptions ?? new PngEncoderOptions(options);
}
}
}

34
src/ImageSharp/Formats/Png/PngFormat.cs

@ -8,40 +8,20 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
/// <summary>
/// Encapsulates the means to encode and decode png images.
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
public class PngFormat : IImageFormat
internal sealed class PngFormat : IImageFormat
{
/// <inheritdoc/>
public string MimeType => "image/png";
public string Name => "PNG";
/// <inheritdoc/>
public string Extension => "png";
public string DefaultMimeType => "image/png";
/// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "png" };
public IEnumerable<string> MimeTypes => PngConstants.MimeTypes;
/// <inheritdoc/>
public IImageDecoder Decoder => new PngDecoder();
/// <inheritdoc/>
public IImageEncoder Encoder => new PngEncoder();
/// <inheritdoc/>
public int HeaderSize => 8;
/// <inheritdoc/>
public bool IsSupportedFileFormat(byte[] header)
{
return header.Length >= this.HeaderSize &&
header[0] == 0x89 &&
header[1] == 0x50 && // P
header[2] == 0x4E && // N
header[3] == 0x47 && // G
header[4] == 0x0D && // CR
header[5] == 0x0A && // LF
header[6] == 0x1A && // EOF
header[7] == 0x0A; // LF
}
public IEnumerable<string> FileExtensions => PngConstants.FileExtensions;
}
}
}

2
src/ImageSharp/Formats/Png/PngHeader.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Represents the png header chunk.
/// </summary>
public sealed class PngHeader
internal sealed class PngHeader
{
/// <summary>
/// Gets or sets the dimension in x-direction of the image in pixels.

43
src/ImageSharp/Formats/Png/PngImageFormatDetector.cs

@ -0,0 +1,43 @@
// <copyright file="PngImageFormatDetector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
/// <summary>
/// Detects png file headers
/// </summary>
public sealed class PngImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
public int HeaderSize => 8;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
if (this.IsSupportedFileFormat(header))
{
return ImageFormats.Png;
}
return null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
// TODO: This should be in constants
return header.Length >= this.HeaderSize &&
header[0] == 0x89 &&
header[1] == 0x50 && // P
header[2] == 0x4E && // N
header[3] == 0x47 && // G
header[4] == 0x0D && // CR
header[5] == 0x0A && // LF
header[6] == 0x1A && // EOF
header[7] == 0x0A; // LF
}
}
}

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

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Provides enumeration of available PNG interlace modes.
/// </summary>
public enum PngInterlaceMode : byte
internal enum PngInterlaceMode : byte
{
/// <summary>
/// Non interlaced

2
src/ImageSharp/Formats/Png/Zlib/IChecksum.cs

@ -12,7 +12,7 @@ namespace ImageSharp.Formats
/// <code>Value</code>. The complete checksum object can also be reset
/// so it can be used again with new data.
/// </summary>
public interface IChecksum
internal interface IChecksum
{
/// <summary>
/// Gets the data checksum computed so far.

2
src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs

@ -9,7 +9,7 @@
/// <summary>
/// Provides methods and properties for deframing streams from PNGs.
/// </summary>
internal class ZlibInflateStream : Stream
internal sealed class ZlibInflateStream : Stream
{
/// <summary>
/// The inner raw memory stream

19
src/ImageSharp/IConfigurationModule.cs

@ -0,0 +1,19 @@
// <copyright file="IConfigurationModule.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Represents an interface that can register image encoders, decoders and image format detectors.
/// </summary>
public interface IConfigurationModule
{
/// <summary>
/// Called when loaded into a configuration object so the module can register items into the configuration.
/// </summary>
/// <param name="configuration">The configuration that will retain the encoders, decodes and mime type detectors.</param>
void Configure(Configuration configuration);
}
}

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

Loading…
Cancel
Save