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_predefined_type_for_member_access = true:warning
dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_field = true:warning
dotnet_style_qualification_for_method = 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
**/CodeCoverage/* **/CodeCoverage/*
docs/ docs/
/samples/AvatarWithRoundedCorner/output

36
ImageSharp.sln

@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26403.7 VisualStudioVersion = 15.0.26430.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
@ -43,7 +43,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\I
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}"
EndProject 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 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}" 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 EndProject
@ -141,6 +147,30 @@ Global
{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x64.Build.0 = Release|x64 {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.ActiveCfg = Release|x86
{F74D25AB-1E5C-4272-9FD3-6DBBD3E207AC}.Release|x86.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -154,5 +184,7 @@ Global
{2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {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} {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 EndGlobalSection
EndGlobal EndGlobal

4
README.md

@ -110,7 +110,7 @@ Setting individual pixel values can be perfomed as follows:
```csharp ```csharp
// Individual pixels // 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; 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/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/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/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 ImageSharp.PixelFormats;
using Processors; using Processors;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Brush represents a logical configuration of a brush which can be used to source pixel colors /// 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using Processors; using Processors;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Provides an implementation of an image brush for painting images within areas. /// 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using Processors; using Processors;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Provides an implementation of a pattern brush for painting patterns. /// 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using Processors; using Processors;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Provides an implementation of a brush that can recolor an image /// 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using Processors; using Processors;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Provides an implementation of a solid brush for painting solid color areas. /// 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 Drawing.Processors;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type. /// 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" /> <ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup> </ItemGroup>
<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"> <PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta001">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>

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

@ -10,6 +10,7 @@ namespace ImageSharp
using Drawing.Brushes; using Drawing.Brushes;
using Drawing.Pens; using Drawing.Pens;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
/// <summary> /// <summary>
@ -27,10 +28,10 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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> /// <summary>
@ -42,10 +43,10 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param> /// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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> /// <summary>
@ -57,7 +58,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param> /// <param name="thickness">The thickness.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points); return source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points);
@ -73,7 +74,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points, options); return source.DrawBeziers(new SolidBrush<TPixel>(color), thickness, points, options);
@ -88,10 +89,10 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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> /// <summary>
@ -102,10 +103,10 @@ namespace ImageSharp
/// <param name="pen">The pen.</param> /// <param name="pen">The pen.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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.Brushes;
using Drawing.Pens; using Drawing.Pens;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
/// <summary> /// <summary>
@ -27,7 +28,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new LinearLineSegment(points)), options); 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="thickness">The thickness.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new Pen<TPixel>(brush, thickness), new Path(new LinearLineSegment(points))); 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="thickness">The thickness.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawLines(new SolidBrush<TPixel>(color), thickness, points); return source.DrawLines(new SolidBrush<TPixel>(color), thickness, points);
@ -73,7 +74,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawLines(new SolidBrush<TPixel>(color), thickness, points, options); return source.DrawLines(new SolidBrush<TPixel>(color), thickness, points, options);
@ -88,7 +89,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(pen, new Path(new LinearLineSegment(points)), options); return source.Draw(pen, new Path(new LinearLineSegment(points)), options);
@ -102,7 +103,7 @@ namespace ImageSharp
/// <param name="pen">The pen.</param> /// <param name="pen">The pen.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(pen, new Path(new LinearLineSegment(points))); 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.Brushes;
using Drawing.Pens; using Drawing.Pens;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
/// <summary> /// <summary>
@ -27,7 +28,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new Pen<TPixel>(brush, thickness), new Polygon(new LinearLineSegment(points)), options); 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="thickness">The thickness.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new Pen<TPixel>(brush, thickness), new Polygon(new LinearLineSegment(points))); 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="thickness">The thickness.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points); return source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points);
@ -73,7 +74,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points, options); return source.DrawPolygon(new SolidBrush<TPixel>(color), thickness, points, options);
@ -87,7 +88,7 @@ namespace ImageSharp
/// <param name="pen">The pen.</param> /// <param name="pen">The pen.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default); 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="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options); 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.Brushes;
using Drawing.Pens; using Drawing.Pens;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type. /// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -24,10 +25,10 @@ namespace ImageSharp
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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> /// <summary>
@ -38,7 +39,7 @@ namespace ImageSharp
/// <param name="pen">The pen.</param> /// <param name="pen">The pen.</param>
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(pen, shape, GraphicsOptions.Default); return source.Draw(pen, shape, GraphicsOptions.Default);
@ -54,7 +55,7 @@ namespace ImageSharp
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new Pen<TPixel>(brush, thickness), shape, options); return source.Draw(new Pen<TPixel>(brush, thickness), shape, options);
@ -69,7 +70,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param> /// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new Pen<TPixel>(brush, thickness), shape); return source.Draw(new Pen<TPixel>(brush, thickness), shape);
@ -85,7 +86,7 @@ namespace ImageSharp
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new SolidBrush<TPixel>(color), thickness, shape, options); return source.Draw(new SolidBrush<TPixel>(color), thickness, shape, options);
@ -100,7 +101,7 @@ namespace ImageSharp
/// <param name="thickness">The thickness.</param> /// <param name="thickness">The thickness.</param>
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Draw(new SolidBrush<TPixel>(color), thickness, shape); 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;
using Drawing.Brushes; using Drawing.Brushes;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
/// <summary> /// <summary>
@ -26,7 +27,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options);
@ -40,7 +41,7 @@ namespace ImageSharp
/// <param name="brush">The brush.</param> /// <param name="brush">The brush.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Fill(brush, new Polygon(new LinearLineSegment(points))); return source.Fill(brush, new Polygon(new LinearLineSegment(points)));
@ -55,7 +56,7 @@ namespace ImageSharp
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Fill(new SolidBrush<TPixel>(color), new Polygon(new LinearLineSegment(points)), options); 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="color">The color.</param>
/// <param name="points">The points.</param> /// <param name="points">The points.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Fill(new SolidBrush<TPixel>(color), new Polygon(new LinearLineSegment(points))); 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;
using Drawing.Brushes; using Drawing.Brushes;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type. /// Extension methods for the <see cref="Image{TPixel}"/> type.
@ -23,10 +24,10 @@ namespace ImageSharp
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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> /// <summary>
@ -37,10 +38,10 @@ namespace ImageSharp
/// <param name="brush">The brush.</param> /// <param name="brush">The brush.</param>
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> 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> /// <summary>
@ -52,7 +53,7 @@ namespace ImageSharp
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Fill(new SolidBrush<TPixel>(color), shape, options); return source.Fill(new SolidBrush<TPixel>(color), shape, options);
@ -66,7 +67,7 @@ namespace ImageSharp
/// <param name="color">The color.</param> /// <param name="color">The color.</param>
/// <param name="shape">The shape.</param> /// <param name="shape">The shape.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
return source.Fill(new SolidBrush<TPixel>(color), shape); 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 SixLabors.Shapes;
using Rectangle = ImageSharp.Rectangle;
/// <summary> /// <summary>
/// A mapping between a <see cref="IPath"/> and a region. /// A mapping between a <see cref="IPath"/> and a region.
/// </summary> /// </summary>

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

@ -8,11 +8,10 @@ namespace ImageSharp.Drawing
using System; using System;
using System.Buffers; using System.Buffers;
using System.Numerics; using System.Numerics;
using ImageSharp.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
using Rectangle = ImageSharp.Rectangle;
/// <summary> /// <summary>
/// A mapping between a <see cref="IPath"/> and a region. /// A mapping between a <see cref="IPath"/> and a region.
/// </summary> /// </summary>
@ -25,7 +24,12 @@ namespace ImageSharp.Drawing
public ShapeRegion(IPath shape) public ShapeRegion(IPath shape)
{ {
this.Shape = shape.AsClosedPath(); 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> /// <summary>
@ -42,24 +46,20 @@ namespace ImageSharp.Drawing
/// <inheritdoc/> /// <inheritdoc/>
public override int Scan(float y, Span<float> buffer) public override int Scan(float y, Span<float> buffer)
{ {
Vector2 start = new Vector2(this.Bounds.Left - 1, y); var start = new PointF(this.Bounds.Left - 1, y);
Vector2 end = new Vector2(this.Bounds.Right + 1, y); var end = new PointF(this.Bounds.Right + 1, y);
Vector2[] innerbuffer = ArrayPool<Vector2>.Shared.Rent(buffer.Length); using (var innerBuffer = new Buffer<PointF>(buffer.Length))
try
{ {
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++) for (int i = 0; i < count; i++)
{ {
buffer[i] = innerbuffer[i].X; buffer[i] = span[i].X;
} }
return count; 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using ImageSharp.Processing; using ImageSharp.Processing;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Combines two images together by blending the pixels. /// 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using ImageSharp.Processing; using ImageSharp.Processing;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Using the bursh as a source of pixels colors blends the brush color with source. /// 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.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using ImageSharp.Processing; using ImageSharp.Processing;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Usinf a brsuh and a shape fills shape with contents of brush the /// 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 namespace ImageSharp.Drawing
{ {
using System; using System;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Represents a region of an image. /// 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) 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> where TPixel : struct, IPixel<TPixel>
{ {
Vector2 dpi = DefaultTextDpi; float dpiX = DefaultTextDpi;
float dpiY = DefaultTextDpi;
if (options.UseImageResolution) 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, ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth, TabWidth = options.TabWidth,

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

@ -12,6 +12,7 @@ namespace ImageSharp
using Drawing.Pens; using Drawing.Pens;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Fonts; using SixLabors.Fonts;
using SixLabors.Primitives;
using SixLabors.Shapes; using SixLabors.Shapes;
/// <summary> /// <summary>
@ -19,7 +20,7 @@ namespace ImageSharp
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
private static readonly Vector2 DefaultTextDpi = new Vector2(72); private static readonly int DefaultTextDpi = 72;
/// <summary> /// <summary>
/// Draws the text onto the the image filled via the brush. /// Draws the text onto the the image filled via the brush.
@ -33,7 +34,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, color, location, TextGraphicsOptions.Default); return source.DrawText(text, font, color, location, TextGraphicsOptions.Default);
@ -52,7 +53,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, Brushes.Solid(color), null, location, options); return source.DrawText(text, font, Brushes.Solid(color), null, location, options);
@ -70,7 +71,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default); return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default);
@ -89,7 +90,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, brush, null, location, options); return source.DrawText(text, font, brush, null, location, options);
@ -107,7 +108,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default); return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default);
@ -126,7 +127,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, null, pen, location, options); return source.DrawText(text, font, null, pen, location, options);
@ -145,7 +146,7 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default); return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default);
@ -165,16 +166,18 @@ namespace ImageSharp
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}" />. /// The <see cref="Image{TPixel}" />.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
Vector2 dpi = DefaultTextDpi; float dpiX = DefaultTextDpi;
float dpiY = DefaultTextDpi;
if (options.UseImageResolution) 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, ApplyKerning = options.ApplyKerning,
TabWidth = options.TabWidth, TabWidth = options.TabWidth,
@ -183,7 +186,7 @@ namespace ImageSharp
VerticalAlignment = options.VerticalAlignment VerticalAlignment = options.VerticalAlignment
}; };
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, location, style); IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, style);
var pathOptions = (GraphicsOptions)options; var pathOptions = (GraphicsOptions)options;
if (brush != null) 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 /// The exception that is thrown when the library tries to load
/// an image, which has an invalid format. /// an image, which has an invalid format.
/// </summary> /// </summary>
public class ImageFormatException : Exception public sealed class ImageFormatException : Exception
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class. /// Initializes a new instance of the <see cref="ImageFormatException"/> class.

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

@ -10,7 +10,7 @@ namespace ImageSharp
/// <summary> /// <summary>
/// The exception that is thrown when an error occurs when applying a process to an image. /// The exception that is thrown when an error occurs when applying a process to an image.
/// </summary> /// </summary>
public class ImageProcessingException : Exception public sealed class ImageProcessingException : Exception
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class. /// 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 System.Runtime.CompilerServices;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Provides common mathematical methods. /// Provides common mathematical methods.

244
src/ImageSharp/Configuration.cs

@ -6,8 +6,8 @@
namespace ImageSharp namespace ImageSharp
{ {
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,22 +17,32 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Provides initialization code which allows extending the library. /// Provides initialization code which allows extending the library.
/// </summary> /// </summary>
public class Configuration public sealed class Configuration
{ {
/// <summary> /// <summary>
/// A lazily initialized configuration default instance. /// A lazily initialized configuration default instance.
/// </summary> /// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(() => CreateDefaultInstance()); private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
/// <summary> /// <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> /// </summary>
private readonly object syncRoot = new object(); private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageFormat"/>. /// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary> /// </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> /// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class. /// Initializes a new instance of the <see cref="Configuration" /> class.
@ -44,12 +54,15 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class. /// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary> /// </summary>
/// <param name="providers">The inital set of image formats.</param> /// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IImageFormat[] providers) 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> /// </summary>
public static Configuration Default { get; } = Lazy.Value; 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> /// <summary>
/// Gets the global parallel options for processing tasks in parallel. /// Gets the global parallel options for processing tasks in parallel.
/// </summary> /// </summary>
public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary> /// <summary>
/// Gets the maximum header size of all formats. /// Gets the maximum header size of all the formats.
/// </summary> /// </summary>
internal int MaxHeaderSize { get; private set; } 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 #if !NETSTANDARD1_1
/// <summary> /// <summary>
/// Gets or sets the fielsystem helper for accessing the local file system. /// Gets or sets the fielsystem helper for accessing the local file system.
@ -81,127 +109,147 @@ namespace ImageSharp
#endif #endif
/// <summary> /// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats. /// Registers a new format provider.
/// </summary> /// </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) public void AddImageFormat(IImageFormat format)
{ {
Guard.NotNull(format, nameof(format)); Guard.NotNull(format, nameof(format));
Guard.NotNull(format.Encoder, nameof(format), "The encoder should not be null."); Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.Decoder, nameof(format), "The decoder should not be null."); Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
Guard.NotNullOrEmpty(format.MimeType, nameof(format), "The mime type should not be null or empty."); this.imageFormats.Add(format);
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.");
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> /// <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> /// </summary>
/// <returns>The default configuration of <see cref="Configuration"/> </returns> /// <param name="mimeType">The mime-type to discover</param>
internal static Configuration CreateDefaultInstance() /// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType)
{ {
Configuration config = new Configuration(); return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
// 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;
} }
/// <summary> /// <summary>
/// Tries the add image format. /// Sets a specific image encoder as the encoder for a specific image format.
/// </summary> /// </summary>
/// <param name="typeName">Name of the type.</param> /// <param name="imageFormat">The image format to register the encoder for.</param>
/// <returns>True if type discoverd and is a valid <see cref="IImageFormat"/></returns> /// <param name="encoder">The encoder to use,</param>
internal bool TryAddImageFormat(string typeName) public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{ {
Type type = Type.GetType(typeName, false); Guard.NotNull(imageFormat, nameof(imageFormat));
if (type != null) Guard.NotNull(encoder, nameof(encoder));
{ this.AddImageFormat(imageFormat);
IImageFormat format = Activator.CreateInstance(type) as IImageFormat; this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
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;
}
}
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> /// <summary>
/// Adds image format. The class is locked to make it thread safe. /// Removes all the registered image format detectors.
/// </summary> /// </summary>
/// <param name="format">The image format.</param> public void ClearImageFormatDetectors()
private void AddImageFormatLocked(IImageFormat format)
{ {
lock (this.syncRoot) this.imageFormatDetectors.Clear();
{ }
if (this.GuardDuplicate(format))
{
this.imageFormatsList.Add(format);
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> /// <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> /// </summary>
/// <param name="format">The image format.</param> /// <returns>The default configuration of <see cref="Configuration"/></returns>
/// <exception cref="ArgumentException">Thrown if a duplicate is added.</exception> internal static Configuration CreateDefaultInstance()
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
private bool GuardDuplicate(IImageFormat format)
{ {
if (!format.SupportedExtensions.Contains(format.Extension, StringComparer.OrdinalIgnoreCase)) return new Configuration(
{ new PngConfigurationModule(),
throw new ArgumentException("The supported extensions should contain the default extension.", nameof(format)); new JpegConfigurationModule(),
} new GifConfigurationModule(),
new BmpConfigurationModule());
}
// ReSharper disable once ConvertClosureToMethodGroup /// <summary>
// Prevents method group allocation /// For the specified mime type find the decoder.
if (format.SupportedExtensions.Any(e => string.IsNullOrWhiteSpace(e))) /// </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 return null;
// extension return false. }
foreach (IImageFormat imageFormat in this.imageFormatsList)
{
if (imageFormat.Extension.Equals(format.Extension, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (imageFormat.SupportedExtensions.Intersect(format.SupportedExtensions, StringComparer.OrdinalIgnoreCase).Any()) /// <summary>
{ /// For the specified mime type find the encoder.
return false; /// </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> /// <summary>
/// Sets max header size. /// Sets the max header size.
/// </summary> /// </summary>
private void SetMaxHeaderSize() 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 namespace ImageSharp.Formats
{ {
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -25,16 +26,16 @@ namespace ImageSharp.Formats
/// Formats will be supported in a later releases. We advise always /// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps. /// to use only 24 Bit Windows bitmaps.
/// </remarks> /// </remarks>
public class BmpDecoder : IImageDecoder public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions
{ {
/// <inheritdoc/> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
Guard.NotNull(stream, "stream"); 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. /// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <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; this.configuration = configuration;
} }

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

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

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

@ -18,22 +18,22 @@ namespace ImageSharp.Formats
internal sealed class BmpEncoderCore internal sealed class BmpEncoderCore
{ {
/// <summary> /// <summary>
/// The options for the encoder. /// The amount to pad each row by.
/// </summary> /// </summary>
private readonly IBmpEncoderOptions options; private int padding;
/// <summary> /// <summary>
/// The amount to pad each row by. /// Gets or sets the number of bits per pixel.
/// </summary> /// </summary>
private int padding; private BmpBitsPerPixel bitsPerPixel;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class. /// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options for the encoder.</param> /// <param name="options">The encoder options</param>
public BmpEncoderCore(IBmpEncoderOptions options) public BmpEncoderCore(IBmpEncoderOptions options)
{ {
this.options = options ?? new BmpEncoderOptions(); this.bitsPerPixel = options.BitsPerPixel;
} }
/// <summary> /// <summary>
@ -49,9 +49,9 @@ namespace ImageSharp.Formats
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
// Cast to int will get the bytes per pixel // 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); 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. // Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
@ -136,7 +136,7 @@ namespace ImageSharp.Formats
{ {
using (PixelAccessor<TPixel> pixels = image.Lock()) using (PixelAccessor<TPixel> pixels = image.Lock())
{ {
switch (this.options.BitsPerPixel) switch (this.bitsPerPixel)
{ {
case BmpBitsPerPixel.Pixel32: case BmpBitsPerPixel.Pixel32:
this.Write32Bit(writer, pixels); 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 /// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first). /// (i.e. least-significant byte first).
/// </remarks> /// </remarks>
internal class BmpFileHeader internal sealed class BmpFileHeader
{ {
/// <summary> /// <summary>
/// Defines of the data structure in the bitmap file. /// 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -8,34 +8,20 @@ namespace ImageSharp.Formats
using System.Collections.Generic; using System.Collections.Generic;
/// <summary> /// <summary>
/// Encapsulates the means to encode and decode bitmap images. /// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary> /// </summary>
public class BmpFormat : IImageFormat internal sealed class BmpFormat : IImageFormat
{ {
/// <inheritdoc/> /// <inheritdoc/>
public string MimeType => "image/bmp"; public string Name => "BMP";
/// <inheritdoc/> /// <inheritdoc/>
public string Extension => "bmp"; public string DefaultMimeType => "image/bmp";
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "bmp", "dip" }; public IEnumerable<string> MimeTypes => BmpConstants.MimeTypes;
/// <inheritdoc/> /// <inheritdoc/>
public IImageDecoder Decoder => new BmpDecoder(); public IEnumerable<string> FileExtensions => BmpConstants.FileExtensions;
/// <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
}
} }
} }

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. /// the screen.
/// <see href="https://en.wikipedia.org/wiki/BMP_file_format"/> /// <see href="https://en.wikipedia.org/wiki/BMP_file_format"/>
/// </summary> /// </summary>
internal class BmpInfoHeader internal sealed class BmpInfoHeader
{ {
/// <summary> /// <summary>
/// Defines of the data structure in the bitmap file. /// 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Encapsulates the options for the <see cref="BmpEncoder"/>. /// Configuration options for use during bmp encoding
/// </summary> /// </summary>
public interface IBmpEncoderOptions : IEncoderOptions /// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
internal interface IBmpEncoderOptions
{ {
/// <summary> /// <summary>
/// Gets the number of bits per pixel. /// 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 namespace ImageSharp.Formats
{ {
using System.Collections.Generic;
using System.Text; using System.Text;
/// <summary> /// <summary>
@ -90,6 +91,16 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Gets the default encoding to use when reading comments. /// Gets the default encoding to use when reading comments.
/// </summary> /// </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 namespace ImageSharp.Formats
{ {
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Decoder for generating an image out of a gif encoded stream. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </summary>
public class GifDecoder : IImageDecoder public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions
{ {
/// <inheritdoc/> /// <summary>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options) /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
where TPixel : struct, IPixel<TPixel> public bool IgnoreMetadata { get; set; } = false;
{
IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options);
return this.Decode<TPixel>(configuration, stream, gifOptions);
}
/// <summary> /// <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> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <param name="configuration">The configuration.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <inheritdoc/>
/// <param name="options">The options for the decoder.</param> public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
/// <returns>The image thats been decoded.</returns>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IGifDecoderOptions options)
where TPixel : struct, IPixel<TPixel> 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 System.Text;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class GifDecoderCore<TPixel> internal sealed class GifDecoderCore<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
@ -25,11 +26,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private readonly byte[] buffer = new byte[16]; private readonly byte[] buffer = new byte[16];
/// <summary>
/// The decoder options.
/// </summary>
private readonly IGifDecoderOptions options;
/// <summary> /// <summary>
/// The global configuration. /// The global configuration.
/// </summary> /// </summary>
@ -83,14 +79,25 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GifDecoderCore{TPixel}"/> class. /// Initializes a new instance of the <see cref="GifDecoderCore{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="configuration">The configuration.</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; 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> /// <summary>
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
@ -268,7 +275,7 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentLength}'"); throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentLength}'");
} }
if (this.options.IgnoreMetadata) if (this.IgnoreMetadata)
{ {
this.currentStream.Seek(length, SeekOrigin.Current); this.currentStream.Seek(length, SeekOrigin.Current);
continue; continue;
@ -279,7 +286,7 @@ namespace ImageSharp.Formats
try try
{ {
this.currentStream.Read(commentsBuffer, 0, length); 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)); this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
} }
finally finally
@ -363,8 +370,6 @@ namespace ImageSharp.Formats
if (this.previousFrame == null) if (this.previousFrame == null)
{ {
this.metaData.Quality = colorTableLength / 3;
// This initializes the image to become fully transparent because the alpha channel is zero. // 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); 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 namespace ImageSharp.Formats
{ {
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary> /// <summary>
/// Image encoder for writing image data to a stream in gif format. /// Image encoder for writing image data to a stream in gif format.
/// </summary> /// </summary>
public class GifEncoder : IImageEncoder public sealed class GifEncoder : IImageEncoder, IGifEncoderOptions
{ {
/// <inheritdoc/> /// <summary>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options) /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
where TPixel : struct, IPixel<TPixel> /// </summary>
{ public bool IgnoreMetadata { get; set; } = false;
IGifEncoderOptions gifOptions = GifEncoderOptions.Create(options);
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> /// <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> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> public int PaletteSize { get; set; } = 0;
/// <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> /// <summary>
/// <param name="options">The options for the encoder.</param> /// Gets or sets the transparency threshold.
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IGifEncoderOptions options) /// </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> where TPixel : struct, IPixel<TPixel>
{ {
GifEncoderCore encoder = new GifEncoderCore(options); GifEncoderCore encoder = new GifEncoderCore(this);
encoder.Encode(image, stream); encoder.Encode(image, stream);
} }
} }

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

@ -9,7 +9,7 @@ namespace ImageSharp.Formats
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using IO; using IO;
@ -25,11 +25,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private readonly byte[] buffer = new byte[16]; private readonly byte[] buffer = new byte[16];
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IGifEncoderOptions options;
/// <summary> /// <summary>
/// The number of bits requires to store the image palette. /// The number of bits requires to store the image palette.
/// </summary> /// </summary>
@ -40,19 +35,44 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private bool hasFrames; 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> /// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class. /// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options for the encoder.</param> /// <param name="options">The options for the encoder.</param>
public GifEncoderCore(IGifEncoderOptions options) public GifEncoderCore(IGifEncoderOptions options)
{ {
this.options = options ?? new GifEncoderOptions(); this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
}
/// <summary> this.quantizer = options.Quantizer;
/// Gets or sets the quantizer for reducing the color count. this.threshold = options.Threshold;
/// </summary> this.paletteSize = options.PaletteSize;
public IQuantizer Quantizer { get; set; } this.ignoreMetadata = options.IgnoreMetadata;
}
/// <summary> /// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. /// 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(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); 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. // Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
// Ensure that quality can be set but has a fallback. // Ensure that pallete size can be set but has a fallback.
int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality; int paletteSize = this.paletteSize;
quality = quality > 0 ? quality.Clamp(1, 256) : 256; paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256;
// Get the number of bits. // 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(); this.hasFrames = image.Frames.Any();
// Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames. // 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; 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); int index = this.GetTransparentIndex(quantized);
@ -111,7 +131,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < image.Frames.Count; i++) for (int i = 0; i < image.Frames.Count; i++)
{ {
ImageFrame<TPixel> frame = image.Frames[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.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame));
this.WriteImageDescriptor(frame, writer); this.WriteImageDescriptor(frame, writer);
@ -240,7 +260,7 @@ namespace ImageSharp.Formats
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer) private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.options.IgnoreMetadata) if (this.ignoreMetadata)
{ {
return; return;
} }
@ -251,7 +271,7 @@ namespace ImageSharp.Formats
return; return;
} }
byte[] comments = this.options.TextEncoding.GetBytes(property.Value); byte[] comments = this.textEncoding.GetBytes(property.Value);
int count = Math.Min(comments.Length, 255); 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; using System.Collections.Generic;
/// <summary> /// <summary>
/// Encapsulates the means to encode and decode gif images. /// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary> /// </summary>
public class GifFormat : IImageFormat internal sealed class GifFormat : IImageFormat
{ {
/// <inheritdoc/> /// <inheritdoc/>
public string Extension => "gif"; public string Name => "GIF";
/// <inheritdoc/> /// <inheritdoc/>
public string MimeType => "image/gif"; public string DefaultMimeType => "image/gif";
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "gif" }; public IEnumerable<string> MimeTypes => GifConstants.MimeTypes;
/// <inheritdoc/> /// <inheritdoc/>
public IImageDecoder Decoder => new GifDecoder(); public IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
/// <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
}
} }
} }

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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Encapsulates the options for the <see cref="GifDecoder"/>. /// Decoder for generating an image out of a gif encoded stream.
/// </summary> /// </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> /// <summary>
/// Gets the encoding that should be used when reading comments. /// Gets the encoding that should be used when reading comments.
/// </summary> /// </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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using ImageSharp.PixelFormats;
using Quantizers; using ImageSharp.Quantizers;
/// <summary> /// <summary>
/// Encapsulates the options for the <see cref="GifEncoder"/>. /// The configuration options used for encoding gifs
/// </summary> /// </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> /// <summary>
/// Gets the encoding that should be used when writing comments. /// Gets the encoding that should be used when writing comments.
/// </summary> /// </summary>
Encoding TextEncoding { get; } Encoding TextEncoding { get; }
/// <summary> /// <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> /// </summary>
/// <remarks>For gifs the value ranges from 1 to 256.</remarks> int PaletteSize { get; }
int Quality { get; }
/// <summary> /// <summary>
/// Gets the transparency threshold. /// Gets the transparency threshold.
@ -33,6 +40,6 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Gets the quantizer for reducing the color count. /// Gets the quantizer for reducing the color count.
/// </summary> /// </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> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</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> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}"/>. /// The <see cref="Image{TPixel}"/>.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
GifEncoder encoder = new GifEncoder(); encoder = encoder ?? new GifEncoder();
encoder.Encode(source, stream, options); encoder.Encode(source, stream);
return source; 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 namespace ImageSharp.Formats
{ {
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -21,9 +22,8 @@ namespace ImageSharp.Formats
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration for the image.</param> /// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</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> /// <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>; where TPixel : struct, IPixel<TPixel>;
} }
} }

4
src/ImageSharp/Formats/IImageEncoder.cs

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

45
src/ImageSharp/Formats/IImageFormat.cs

@ -8,53 +8,28 @@ namespace ImageSharp.Formats
using System.Collections.Generic; using System.Collections.Generic;
/// <summary> /// <summary>
/// Encapsulates a supported image format, providing means to encode and decode an image. /// Describes an image format.
/// Individual formats implements in this interface must be registered in the <see cref="Configuration"/>
/// </summary> /// </summary>
public interface IImageFormat public interface IImageFormat
{ {
/// <summary> /// <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> /// </summary>
string MimeType { get; } string Name { get; }
/// <summary> /// <summary>
/// Gets the default file extension for this format. /// Gets the default mimetype that the image foramt uses
/// </summary> /// </summary>
string Extension { get; } string DefaultMimeType { get; }
/// <summary> /// <summary>
/// Gets the supported file extensions for this format. /// Gets all the mimetypes that have been used by this image foramt.
/// </summary> /// </summary>
/// <returns> IEnumerable<string> MimeTypes { get; }
/// The supported file extension.
/// </returns>
IEnumerable<string> SupportedExtensions { get; }
/// <summary> /// <summary>
/// Gets the image encoder for encoding an image from a stream. /// Gets the file extensions this image format commonly uses.
/// </summary> /// </summary>
IImageEncoder Encoder { get; } IEnumerable<string> FileExtensions { 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);
} }
} }

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 System.Buffers;
using ImageSharp.Memory; using ImageSharp.Memory;
using SixLabors.Primitives;
/// <summary> /// <summary>
/// Represents an image made up of three color components (luminance, blue chroma, red chroma) /// 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Encapsulates the shared decoder options. /// Image decoder for generating an image out of a jpg stream.
/// </summary> /// </summary>
public interface IDecoderOptions internal interface IJpegDecoderOptions
{ {
/// <summary> /// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded. /// 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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Encapsulates the options for the <see cref="JpegEncoder"/>. /// Encoder for writing the data image to a stream in jpeg format.
/// </summary> /// </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> /// <summary>
/// Gets the quality, that will be used to encode the image. Quality /// Gets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min). /// 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> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</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> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}"/>. /// The <see cref="Image{TPixel}"/>.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
JpegEncoder encoder = new JpegEncoder(); encoder = encoder ?? new JpegEncoder();
encoder.Encode(source, stream, options); encoder.Encode(source, stream);
return source; 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 namespace ImageSharp.Formats
{ {
using System.Collections.Generic;
/// <summary> /// <summary>
/// Defines jpeg constants defined in the specification. /// Defines jpeg constants defined in the specification.
/// </summary> /// </summary>
@ -15,6 +17,16 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
public const ushort MaxLength = 65535; 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> /// <summary>
/// Represents high detail chroma horizontal subsampling. /// Represents high detail chroma horizontal subsampling.
/// </summary> /// </summary>

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

@ -6,6 +6,7 @@
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -13,15 +14,20 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Image decoder for generating an image out of a jpg stream. /// Image decoder for generating an image out of a jpg stream.
/// </summary> /// </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/> /// <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> where TPixel : struct, IPixel<TPixel>
{ {
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, "stream");
using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration)) using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this))
{ {
return decoder.Decode<TPixel>(stream); return decoder.Decode<TPixel>(stream);
} }

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

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

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

@ -5,6 +5,8 @@
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -12,16 +14,25 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Encoder for writing the data image to a stream in jpeg format. /// Encoder for writing the data image to a stream in jpeg format.
/// </summary> /// </summary>
public class JpegEncoder : IImageEncoder public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions
{ {
/// <inheritdoc/> /// <summary>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options) /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
where TPixel : struct, IPixel<TPixel> /// </summary>
{ public bool IgnoreMetadata { get; set; }
IJpegEncoderOptions gifOptions = JpegEncoderOptions.Create(options);
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> /// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. /// 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> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param> /// <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="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)
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IJpegEncoderOptions options) where TPixel : struct, IPixel<TPixel>
where TPixel : struct, IPixel<TPixel>
{ {
JpegEncoderCore encode = new JpegEncoderCore(options); var encoder = new JpegEncoderCore(this);
encode.Encode(image, stream); encoder.Encode(image, stream);
} }
} }
} }

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

@ -17,7 +17,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream as a jpeg. /// Image encoder for writing an image to a stream as a jpeg.
/// </summary> /// </summary>
internal unsafe class JpegEncoderCore internal sealed unsafe class JpegEncoderCore
{ {
/// <summary> /// <summary>
/// The number of quantization tables. /// The number of quantization tables.
@ -124,11 +124,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private readonly byte[] huffmanBuffer = new byte[179]; private readonly byte[] huffmanBuffer = new byte[179];
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IJpegEncoderOptions options;
/// <summary> /// <summary>
/// The accumulated bits to write to the stream. /// The accumulated bits to write to the stream.
/// </summary> /// </summary>
@ -155,17 +150,38 @@ namespace ImageSharp.Formats
private Stream outputStream; private Stream outputStream;
/// <summary> /// <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> /// </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> /// <summary>
/// Initializes a new instance of the <see cref="JpegEncoderCore"/> class. /// Initializes a new instance of the <see cref="JpegEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options for the encoder.</param> /// <param name="options">The options</param>
public JpegEncoderCore(IJpegEncoderOptions options) 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> /// <summary>
@ -186,21 +202,13 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); 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.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. // Convert from a quality rating to a scaling factor.
int scale; int scale;
if (quality < 50) if (this.quality < 50)
{ {
scale = 5000 / quality; scale = 5000 / quality;
} }
@ -788,7 +796,7 @@ namespace ImageSharp.Formats
private void WriteProfiles<TPixel>(Image<TPixel> image) private void WriteProfiles<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.options.IgnoreMetadata) if (this.ignoreMetadata)
{ {
return; 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; using System.Collections.Generic;
/// <summary> /// <summary>
/// Encapsulates the means to encode and decode jpeg images. /// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary> /// </summary>
public class JpegFormat : IImageFormat internal sealed class JpegFormat : IImageFormat
{ {
/// <inheritdoc/> /// <inheritdoc/>
public string MimeType => "image/jpeg"; public string Name => "JPEG";
/// <inheritdoc/> /// <inheritdoc/>
public string Extension => "jpg"; public string DefaultMimeType => "image/jpeg";
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "jpg", "jpeg", "jfif" }; public IEnumerable<string> MimeTypes => JpegConstants.MimeTypes;
/// <inheritdoc/> /// <inheritdoc/>
public IImageDecoder Decoder => new JpegDecoder(); public IEnumerable<string> FileExtensions => JpegConstants.FileExtensions;
/// <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;
}
} }
} }

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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using ImageSharp.PixelFormats;
/// <summary> /// <summary>
/// Encapsulates the options for the <see cref="PngDecoder"/>. /// The optioas for decoding png images
/// </summary> /// </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> /// <summary>
/// Gets the encoding that should be used when reading text chunks. /// Gets the encoding that should be used when reading text chunks.
/// </summary> /// </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. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageSharp.Formats namespace ImageSharp.Formats
{ {
using Quantizers; using System.Collections.Generic;
using System.IO;
using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary> /// <summary>
/// Encapsulates the options for the <see cref="PngEncoder"/>. /// The options availible for manipulating the encoder pipeline
/// </summary> /// </summary>
public interface IPngEncoderOptions : IEncoderOptions internal interface IPngEncoderOptions
{ {
/// <summary> /// <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> /// </summary>
int Quality { get; } int PaletteSize { get; }
/// <summary> /// <summary>
/// Gets the png color type /// Gets the png color type
@ -24,19 +33,20 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Gets the compression level 1-9. /// Gets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary> /// </summary>
int CompressionLevel { get; } int CompressionLevel { get; }
/// <summary> /// <summary>
/// Gets the gamma value, that will be written /// Gets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property /// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true. /// is set to true. The default value is 2.2F.
/// </summary> /// </summary>
/// <value>The gamma value of the image.</value> /// <value>The gamma value of the image.</value>
float Gamma { get; } float Gamma { get; }
/// <summary> /// <summary>
/// Gets quantizer for reducing the color count. /// Gets quantizer for reducing the color count.
/// </summary> /// </summary>
IQuantizer Quantizer { get; } IQuantizer Quantizer { get; }
@ -47,7 +57,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Gets a value indicating whether this instance should write /// 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> /// </summary>
bool WriteGamma { get; } bool WriteGamma { get; }
} }

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

@ -38,16 +38,16 @@ namespace ImageSharp
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</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> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns> /// <returns>
/// The <see cref="Image{TPixel}"/>. /// The <see cref="Image{TPixel}"/>.
/// </returns> /// </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> where TPixel : struct, IPixel<TPixel>
{ {
PngEncoder encoder = new PngEncoder(); encoder = encoder ?? new PngEncoder();
encoder.Encode(source, stream, options); encoder.Encode(source, stream);
return source; 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 namespace ImageSharp.Formats
{ {
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
/// <summary> /// <summary>
@ -30,17 +31,17 @@ namespace ImageSharp.Formats
/// </list> /// </list>
/// </para> /// </para>
/// </remarks> /// </remarks>
public class PngDecoder : IImageDecoder public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions
{ {
/// <inheritdoc/> /// <summary>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, IDecoderOptions options) /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
where TPixel : struct, IPixel<TPixel> public bool IgnoreMetadata { get; set; }
{
IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options);
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> /// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TPixel}"/>. /// 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> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration for the image.</param> /// <param name="configuration">The configuration for the image.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</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> /// <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> 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.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using ImageSharp.Memory; using ImageSharp.Memory;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
@ -20,7 +20,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Performs the png decoding operation. /// Performs the png decoding operation.
/// </summary> /// </summary>
internal class PngDecoderCore internal sealed class PngDecoderCore
{ {
/// <summary> /// <summary>
/// The dictionary of available color types. /// The dictionary of available color types.
@ -28,10 +28,10 @@ namespace ImageSharp.Formats
private static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>() private static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>()
{ {
[PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8 }, [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.Palette] = new byte[] { 1, 2, 4, 8 },
[PngColorType.GrayscaleWithAlpha] = new byte[] { 8 }, [PngColorType.GrayscaleWithAlpha] = new byte[] { 8 },
[PngColorType.RgbWithAlpha] = new byte[] { 8 }, [PngColorType.RgbWithAlpha] = new byte[] { 8, 16 }
}; };
/// <summary> /// <summary>
@ -74,11 +74,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private readonly char[] chars = new char[4]; private readonly char[] chars = new char[4];
/// <summary>
/// The decoder options.
/// </summary>
private readonly IPngDecoderOptions options;
/// <summary> /// <summary>
/// Reusable crc for validating chunks. /// Reusable crc for validating chunks.
/// </summary> /// </summary>
@ -147,29 +142,40 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The current pass for an interlaced PNG /// The current pass for an interlaced PNG
/// </summary> /// </summary>
private int pass = 0; private int pass;
/// <summary> /// <summary>
/// The current number of bytes read in the current scanline /// The current number of bytes read in the current scanline
/// </summary> /// </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> /// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class. /// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="configuration">The configuration.</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.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> /// <summary>
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
@ -221,7 +227,6 @@ namespace ImageSharp.Formats
byte[] pal = new byte[currentChunk.Length]; byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length);
this.palette = pal; this.palette = pal;
metadata.Quality = pal.Length / 3;
break; break;
case PngChunkTypes.PaletteAlpha: case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length]; byte[] alpha = new byte[currentChunk.Length];
@ -325,11 +330,6 @@ namespace ImageSharp.Formats
private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image) private void InitializeImage<TPixel>(ImageMetaData metadata, out Image<TPixel> image)
where TPixel : struct, IPixel<TPixel> 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); image = new Image<TPixel>(this.configuration, this.header.Width, this.header.Height, metadata);
this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
@ -349,7 +349,7 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
private int CalculateBytesPerPixel() private int CalculateBytesPerPixel()
{ {
switch (this.PngColorType) switch (this.pngColorType)
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
return 1; return 1;
@ -361,10 +361,20 @@ namespace ImageSharp.Formats
return 1; return 1;
case PngColorType.Rgb: case PngColorType.Rgb:
if (this.header.BitDepth == 16)
{
return 6;
}
return 3; return 3;
// PngColorType.RgbWithAlpha: case PngColorType.RgbWithAlpha:
default: default:
if (this.header.BitDepth == 16)
{
return 8;
}
return 4; return 4;
} }
} }
@ -378,15 +388,16 @@ namespace ImageSharp.Formats
/// </returns> /// </returns>
private int CalculateScanlineLength(int width) private int CalculateScanlineLength(int width)
{ {
int mod = this.header.BitDepth == 16 ? 16 : 8;
int scanlineLength = width * this.header.BitDepth * this.bytesPerPixel; int scanlineLength = width * this.header.BitDepth * this.bytesPerPixel;
int amount = scanlineLength % 8; int amount = scanlineLength % mod;
if (amount != 0) if (amount != 0)
{ {
scanlineLength += 8 - amount; scanlineLength += mod - amount;
} }
return scanlineLength / 8; return scanlineLength / mod;
} }
/// <summary> /// <summary>
@ -566,7 +577,7 @@ namespace ImageSharp.Formats
Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.GetRowSpan(this.currentRow);
var scanlineBuffer = new Span<byte>(defilteredScanline, 1); var scanlineBuffer = new Span<byte>(defilteredScanline, 1);
switch (this.PngColorType) switch (this.pngColorType)
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
@ -589,7 +600,7 @@ namespace ImageSharp.Formats
byte intensity = defilteredScanline[offset]; byte intensity = defilteredScanline[offset];
byte alpha = defilteredScanline[offset + this.bytesPerSample]; byte alpha = defilteredScanline[offset + this.bytesPerSample];
color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha));
rowSpan[x] = color; rowSpan[x] = color;
} }
@ -603,18 +614,59 @@ namespace ImageSharp.Formats
case PngColorType.Rgb: 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; break;
case PngColorType.RgbWithAlpha: 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; 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> /// <summary>
/// Processes a scanline that uses a palette /// Processes a scanline that uses a palette
/// </summary> /// </summary>
@ -625,10 +677,10 @@ namespace ImageSharp.Formats
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] palette = this.palette; byte[] pal = this.palette;
var color = default(TPixel); var color = default(TPixel);
Rgba32 rgba = default(Rgba32); var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{ {
@ -643,7 +695,7 @@ namespace ImageSharp.Formats
if (rgba.A > 0) if (rgba.A > 0)
{ {
rgba.Rgb = palette.GetRgb24(pixelOffset); rgba.Rgb = pal.GetRgb24(pixelOffset);
} }
else else
{ {
@ -663,7 +715,7 @@ namespace ImageSharp.Formats
int index = newScanline[x + 1]; int index = newScanline[x + 1];
int pixelOffset = index * 3; int pixelOffset = index * 3;
rgba.Rgb = palette.GetRgb24(pixelOffset); rgba.Rgb = pal.GetRgb24(pixelOffset);
color.PackFromRgba32(rgba); color.PackFromRgba32(rgba);
row[x] = color; row[x] = color;
@ -684,11 +736,14 @@ namespace ImageSharp.Formats
{ {
var color = default(TPixel); var color = default(TPixel);
switch (this.PngColorType) switch (this.pngColorType)
{ {
case PngColorType.Grayscale: case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); 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++) for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++)
{ {
byte intensity = (byte)(newScanline1[o] * factor); byte intensity = (byte)(newScanline1[o] * factor);
@ -712,8 +767,11 @@ namespace ImageSharp.Formats
case PngColorType.Palette: case PngColorType.Palette:
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] newScanline = ToArrayByBitsLength(
Rgba32 rgba = default(Rgba32); defilteredScanline,
this.bytesPerScanline,
this.header.BitDepth);
var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{ {
@ -760,29 +818,75 @@ namespace ImageSharp.Formats
case PngColorType.Rgb: case PngColorType.Rgb:
rgba.A = 255; 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]; int length = this.header.Width * 3;
rgba.G = defilteredScanline[o + this.bytesPerSample]; using (var compressed = new Buffer<byte>(length))
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; {
// 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); color.PackFromRgba32(rgba);
rowSpan[x] = color; 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; break;
case PngColorType.RgbWithAlpha: 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]; int length = this.header.Width * 4;
rgba.G = defilteredScanline[o + this.bytesPerSample]; using (var compressed = new Buffer<byte>(length))
rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; {
rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)]; // 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); color.PackFromRgba32(rgba);
rowSpan[x] = color; 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; break;
@ -797,7 +901,7 @@ namespace ImageSharp.Formats
/// <param name="length">The maximum length to read.</param> /// <param name="length">The maximum length to read.</param>
private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length) private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length)
{ {
if (this.options.IgnoreMetadata) if (this.ignoreMetadata)
{ {
return; return;
} }
@ -813,8 +917,8 @@ namespace ImageSharp.Formats
} }
} }
string name = this.options.TextEncoding.GetString(data, 0, zeroIndex); string name = this.textEncoding.GetString(data, 0, zeroIndex);
string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1); string value = this.textEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
metadata.Properties.Add(new ImageProperty(name, value)); 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."); 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> /// <summary>
@ -985,13 +1089,13 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Returns the correct number of columns for each interlaced pass. /// Returns the correct number of columns for each interlaced pass.
/// </summary> /// </summary>
/// <param name="pass">Th current pass index</param> /// <param name="passIndex">Th current pass index</param>
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ComputeColumnsAdam7(int pass) private int ComputeColumnsAdam7(int passIndex)
{ {
int width = this.header.Width; int width = this.header.Width;
switch (pass) switch (passIndex)
{ {
case 0: return (width + 7) / 8; case 0: return (width + 7) / 8;
case 1: return (width + 3) / 8; case 1: return (width + 3) / 8;
@ -1000,7 +1104,7 @@ namespace ImageSharp.Formats
case 4: return (width + 1) / 2; case 4: return (width + 1) / 2;
case 5: return width / 2; case 5: return width / 2;
case 6: return width; 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 namespace ImageSharp.Formats
{ {
using System.Collections.Generic;
using System.IO; using System.IO;
using ImageSharp.PixelFormats; using ImageSharp.PixelFormats;
using ImageSharp.Quantizers;
/// <summary> /// <summary>
/// Image encoder for writing image data to a stream in png format. /// Image encoder for writing image data to a stream in png format.
/// </summary> /// </summary>
public class PngEncoder : IImageEncoder public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions
{ {
/// <inheritdoc/> /// <summary>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IEncoderOptions options) /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
where TPixel : struct, IPixel<TPixel> /// </summary>
{ public bool IgnoreMetadata { get; set; }
IPngEncoderOptions pngOptions = PngEncoderOptions.Create(options);
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> /// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. /// 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> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param> /// <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="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)
public void Encode<TPixel>(Image<TPixel> image, Stream stream, IPngEncoderOptions options)
where TPixel : struct, IPixel<TPixel> 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> /// </summary>
private readonly Crc32 crc = new Crc32(); private readonly Crc32 crc = new Crc32();
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IPngEncoderOptions options;
/// <summary> /// <summary>
/// Contains the raw pixel data from an indexed image. /// Contains the raw pixel data from an indexed image.
/// </summary> /// </summary>
@ -113,11 +108,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private Buffer<byte> paeth; private Buffer<byte> paeth;
/// <summary>
/// The quality of output for images.
/// </summary>
private int quality;
/// <summary> /// <summary>
/// The png color type. /// The png color type.
/// </summary> /// </summary>
@ -128,13 +118,50 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private IQuantizer quantizer; 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> /// <summary>
/// Initializes a new instance of the <see cref="PngEncoderCore"/> class. /// Initializes a new instance of the <see cref="PngEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options for the encoder.</param> /// <param name="options">The options for influancing the encoder</param>
public PngEncoderCore(IPngEncoderOptions options) 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> /// <summary>
@ -164,27 +191,20 @@ namespace ImageSharp.Formats
stream.Write(this.chunkDataBuffer, 0, 8); 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. // Set correct color type if the color count is 256 or less.
if (this.quality <= 256) if (this.paletteSize <= 256)
{ {
this.pngColorType = PngColorType.Palette; 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. // Set correct bit depth.
this.bitDepth = this.quality <= 256 this.bitDepth = this.paletteSize <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.quality).Clamp(1, 8) ? (byte)ImageMaths.GetBitsNeededForColorDepth(this.paletteSize).Clamp(1, 8)
: (byte)8; : (byte)8;
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk // 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) private QuantizedImage<TPixel> WritePaletteChunk<TPixel>(Stream stream, PngHeader header, ImageBase<TPixel> image)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.quality > 256) if (this.paletteSize > 256)
{ {
return null; return null;
} }
@ -525,7 +545,7 @@ namespace ImageSharp.Formats
} }
// Quantize the image returning a palette. This boxing is icky. // 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. // Grab the palette and write it to the stream.
TPixel[] palette = quantized.Palette; TPixel[] palette = quantized.Palette;
@ -552,7 +572,7 @@ namespace ImageSharp.Formats
colorTable[offset + 1] = bytes[1]; colorTable[offset + 1] = bytes[1];
colorTable[offset + 2] = bytes[2]; colorTable[offset + 2] = bytes[2];
if (alpha > this.options.Threshold) if (alpha > this.threshold)
{ {
alpha = 255; alpha = 255;
} }
@ -610,9 +630,9 @@ namespace ImageSharp.Formats
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
private void WriteGammaChunk(Stream stream) 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); byte[] size = BitConverter.GetBytes(gammaValue);
@ -655,7 +675,7 @@ namespace ImageSharp.Formats
try try
{ {
memoryStream = new MemoryStream(); 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++) 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; using System.Collections.Generic;
/// <summary> /// <summary>
/// Encapsulates the means to encode and decode png images. /// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary> /// </summary>
public class PngFormat : IImageFormat internal sealed class PngFormat : IImageFormat
{ {
/// <inheritdoc/> /// <inheritdoc/>
public string MimeType => "image/png"; public string Name => "PNG";
/// <inheritdoc/> /// <inheritdoc/>
public string Extension => "png"; public string DefaultMimeType => "image/png";
/// <inheritdoc/> /// <inheritdoc/>
public IEnumerable<string> SupportedExtensions => new string[] { "png" }; public IEnumerable<string> MimeTypes => PngConstants.MimeTypes;
/// <inheritdoc/> /// <inheritdoc/>
public IImageDecoder Decoder => new PngDecoder(); public IEnumerable<string> FileExtensions => PngConstants.FileExtensions;
/// <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
}
} }
} }

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

@ -8,7 +8,7 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// Represents the png header chunk. /// Represents the png header chunk.
/// </summary> /// </summary>
public sealed class PngHeader internal sealed class PngHeader
{ {
/// <summary> /// <summary>
/// Gets or sets the dimension in x-direction of the image in pixels. /// 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> /// <summary>
/// Provides enumeration of available PNG interlace modes. /// Provides enumeration of available PNG interlace modes.
/// </summary> /// </summary>
public enum PngInterlaceMode : byte internal enum PngInterlaceMode : byte
{ {
/// <summary> /// <summary>
/// Non interlaced /// 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 /// <code>Value</code>. The complete checksum object can also be reset
/// so it can be used again with new data. /// so it can be used again with new data.
/// </summary> /// </summary>
public interface IChecksum internal interface IChecksum
{ {
/// <summary> /// <summary>
/// Gets the data checksum computed so far. /// Gets the data checksum computed so far.

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

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