diff --git a/.gitignore b/.gitignore
index c2e6f7d53..494281897 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,9 @@ bld/
# Visual Studo 2015 cache/options directory
.vs/
+# Jetbrains Rider cache/options directory
+.idea/
+
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
diff --git a/.travis.yml b/.travis.yml
index 70501a484..54e4dee2f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,7 @@ matrix:
- os: linux # Ubuntu 14.04
dist: trusty
sudo: required
- dotnet: 1.0.4
+ dotnet: 2.1.4
mono: latest
# - os: osx # OSX 10.11
# osx_image: xcode7.3.1
@@ -21,7 +21,7 @@ branches:
script:
- git submodule -q update --init
- dotnet restore
- - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1"
+ - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp2.0"
env:
global:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index c9c7453f6..c772e647c 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
- "program": "${workspaceRoot}/samples/AvatarWithRoundedCorner/bin/Debug/netcoreapp1.1/AvatarWithRoundedCorner.dll",
+ "program": "${workspaceRoot}/tests/ImageSharp.Benchmarks/bin/Debug/netcoreapp2.0/ImageSharp.Benchmarks.dll",
"args": [],
"cwd": "${workspaceRoot}/samples/AvatarWithRoundedCorner",
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 4a7b35ac2..82aaa2f8d 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -16,13 +16,13 @@
{
"taskName": "build benchmark",
"suppressTaskName": true,
- "args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp1.1", "-c", "Release" ],
+ "args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp2.0", "-c", "Release" ],
"showOutput": "always",
"problemMatcher": "$msCompile"
},
{
"taskName": "test",
- "args": ["tests/ImageSharp.Tests/ImageSharp.Tests.csproj", "-c", "release", "-f", "netcoreapp1.1"],
+ "args": ["tests/ImageSharp.Tests/ImageSharp.Tests.csproj", "-c", "release", "-f", "netcoreapp2.0"],
"isTestCommand": true,
"showOutput": "always",
"problemMatcher": "$msCompile"
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 4ea89dd45..3ff5b09d4 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -43,12 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\I
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}"
EndProject
-Project("{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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}"
EndProject
Global
@@ -112,30 +106,6 @@ Global
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU
- {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
{561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -158,8 +128,6 @@ Global
{2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {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}
{561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/ImageSharp.v2.ncrunchsolution b/ImageSharp.v2.ncrunchsolution
deleted file mode 100644
index b98737f1c..000000000
--- a/ImageSharp.v2.ncrunchsolution
+++ /dev/null
@@ -1,14 +0,0 @@
-
- 1
- false
- false
- true
- UseDynamicAnalysis
- UseStaticAnalysis
- UseStaticAnalysis
- UseStaticAnalysis
- UseDynamicAnalysis
-
-
-
-
\ No newline at end of file
diff --git a/ImageSharp.v3.ncrunchsolution b/ImageSharp.v3.ncrunchsolution
deleted file mode 100644
index 10420ac91..000000000
--- a/ImageSharp.v3.ncrunchsolution
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- True
- True
-
-
\ No newline at end of file
diff --git a/NuGet.config b/NuGet.config
index b2c967cc9..322105d4d 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -1,6 +1,7 @@
+
diff --git a/config.wyam b/config.wyam
deleted file mode 100644
index 3a4b64c54..000000000
--- a/config.wyam
+++ /dev/null
@@ -1,4 +0,0 @@
-#recipe Docs
-Settings[Keys.Host] = "imagesharp.org";
-Settings[Keys.Title] = "Image Sharp";
-FileSystem.OutputPath = "./docs";
\ No newline at end of file
diff --git a/packages.xml b/packages.xml
deleted file mode 100644
index c7ff4de4c..000000000
--- a/packages.xml
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj b/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj
deleted file mode 100644
index e000aacf1..000000000
--- a/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- Exe
- netcoreapp1.1
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs
deleted file mode 100644
index 087bbc29d..000000000
--- a/samples/AvatarWithRoundedCorner/Program.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
-using SixLabors.Primitives;
-using SixLabors.Shapes;
-
-namespace AvatarWithRoundedCorner
-{
- static class Program
- {
- static void Main(string[] args)
- {
- System.IO.Directory.CreateDirectory("output");
- using (var img = Image.Load("fb.jpg"))
- {
- // as generate returns a new IImage make sure we dispose of it
- using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 20)))
- {
- destRound.Save("output/fb.png");
- }
-
- using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 100)))
- {
- destRound.Save("output/fb-round.png");
- }
-
- using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 150)))
- {
- destRound.Save("output/fb-rounder.png");
- }
-
- using (Image destRound = img.CloneAndConvertToAvatarWithoutApply(new Size(200, 200), 150))
- {
- destRound.Save("output/fb-rounder-without-apply.png");
- }
-
- // the original `img` object has not been altered at all.
- }
- }
-
- // 1. The short way:
- // Implements a full image mutating pipeline operating on IImageProcessingContext
- // We need the dimensions of the resized image to deduce 'IPathCollection' needed to build the corners,
- // so we implement an "inline" image processor by utilizing 'ImageExtensions.Apply()'
- private static IImageProcessingContext ConvertToAvatar(this IImageProcessingContext processingContext, Size size, float cornerRadius)
- {
- return processingContext.Resize(new ResizeOptions
- {
- Size = size,
- Mode = ResizeMode.Crop
- }).Apply(i => ApplyRoundedCorners(i, cornerRadius));
- }
-
- // 2. A more verbose way, avoiding 'Apply()':
- // First we create a resized clone of the image, then we draw the corners on that instance with Mutate().
- private static Image CloneAndConvertToAvatarWithoutApply(this Image image, Size size, float cornerRadius)
- {
- Image result = image.Clone(
- ctx => ctx.Resize(
- new ResizeOptions
- {
- Size = size,
- Mode = ResizeMode.Crop
- }));
-
- ApplyRoundedCorners(result, cornerRadius);
- return result;
- }
-
- // This method can be seen as an inline implementation of an `IImageProcessor`:
- // (The combination of `IImageOperations.Apply()` + this could be replaced with an `IImageProcessor`)
- public static void ApplyRoundedCorners(Image img, float cornerRadius)
- {
- IPathCollection corners = BuildCorners(img.Width, img.Height, cornerRadius);
-
- // mutating in here as we already have a cloned original
- img.Mutate(x => x.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true)
- {
- BlenderMode = 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 RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius);
-
- // then cut out of the square a circle so we are left with a corner
- IPath cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius));
-
- // corner is now a corner shape positions top left
- //lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image
- var center = new Vector2(imageWidth / 2F, imageHeight / 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
- IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0);
- IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos);
- IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos);
-
- return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/AvatarWithRoundedCorner/fb.jpg b/samples/AvatarWithRoundedCorner/fb.jpg
deleted file mode 100644
index 7241890e2..000000000
--- a/samples/AvatarWithRoundedCorner/fb.jpg
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:93bb4d6281dc1e845db57e836e0dca30b7a4062e81044efb27ad4d8b1a33130c
-size 15787
diff --git a/samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj b/samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj
deleted file mode 100644
index 5797be0f5..000000000
--- a/samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- Exe
- netcoreapp1.1
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/samples/ChangeDefaultEncoderOptions/Program.cs b/samples/ChangeDefaultEncoderOptions/Program.cs
deleted file mode 100644
index a8fbd7599..000000000
--- a/samples/ChangeDefaultEncoderOptions/Program.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Jpeg;
-
-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 JpegEncoder()
- {
- Quality = 90,
- IgnoreMetadata = true
- });
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
index 2d29e23fe..320c94c96 100644
--- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
@@ -122,24 +122,27 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
internal override void Apply(Span scanline, int x, int y)
{
// Create a span for colors
- using (var amountBuffer = new Buffer(scanline.Length))
- using (var overlay = new Buffer(scanline.Length))
+ using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length))
+ using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length))
{
+ Span amountSpan = amountBuffer.Span;
+ Span overlaySpan = overlay.Span;
+
int sourceY = (y - this.offsetY) % this.yLength;
int offsetX = x - this.offsetX;
Span sourceRow = this.source.GetPixelRowSpan(sourceY);
for (int i = 0; i < scanline.Length; i++)
{
- amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
+ amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int sourceX = (i + offsetX) % this.xLength;
TPixel pixel = sourceRow[sourceX];
- overlay[i] = pixel;
+ overlaySpan[i] = pixel;
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
+ this.Blender.Blend(this.source.MemoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
index 844df0e0e..cc22b2639 100644
--- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs
@@ -152,19 +152,24 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
internal override void Apply(Span scanline, int x, int y)
{
int patternY = y % this.pattern.Height;
- using (var amountBuffer = new Buffer(scanline.Length))
- using (var overlay = new Buffer(scanline.Length))
+ MemoryManager memoryManager = this.Target.MemoryManager;
+
+ using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length))
+ using (IBuffer overlay = memoryManager.Allocate(scanline.Length))
{
+ Span amountSpan = amountBuffer.Span;
+ Span overlaySpan = overlay.Span;
+
for (int i = 0; i < scanline.Length; i++)
{
- amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
+ amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1);
int patternX = (x + i) % this.pattern.Width;
- overlay[i] = this.pattern[patternY, patternX];
+ overlaySpan[i] = this.pattern[patternY, patternX];
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
+ this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
index ca6f7630d..d8ea43558 100644
--- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
+++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
@@ -65,21 +65,26 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors
/// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.
internal virtual void Apply(Span scanline, int x, int y)
{
- using (var amountBuffer = new Buffer(scanline.Length))
- using (var overlay = new Buffer(scanline.Length))
+ MemoryManager memoryManager = this.Target.MemoryManager;
+
+ using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length))
+ using (IBuffer overlay = memoryManager.Allocate(scanline.Length))
{
+ Span amountSpan = amountBuffer.Span;
+ Span overlaySpan = overlay.Span;
+
for (int i = 0; i < scanline.Length; i++)
{
if (this.Options.BlendPercentage < 1)
{
- amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
+ amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
- overlay[i] = this[x + i, y];
+ overlaySpan[i] = this[x + i, y];
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
+ this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
index ba2fca4e4..39afd965c 100644
--- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs
@@ -144,22 +144,27 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
///
internal override void Apply(Span scanline, int x, int y)
{
- using (var amountBuffer = new Buffer(scanline.Length))
- using (var overlay = new Buffer(scanline.Length))
+ MemoryManager memoryManager = this.Target.MemoryManager;
+
+ using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length))
+ using (IBuffer overlay = memoryManager.Allocate(scanline.Length))
{
+ Span amountSpan = amountBuffer.Span;
+ Span overlaySpan = overlay.Span;
+
for (int i = 0; i < scanline.Length; i++)
{
- amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
+ amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
int offsetX = x + i;
// no doubt this one can be optermised further but I can't imagine its
// actually being used and can probably be removed/interalised for now
- overlay[i] = this[offsetX, y];
+ overlaySpan[i] = this[offsetX, y];
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
- this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer);
+ this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
index 658164339..9630c707e 100644
--- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs
@@ -61,17 +61,14 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options)
: base(source, options)
{
- this.Colors = new Buffer(source.Width);
- for (int i = 0; i < this.Colors.Length; i++)
- {
- this.Colors[i] = color;
- }
+ this.Colors = source.MemoryManager.Allocate(source.Width);
+ this.Colors.Span.Fill(color);
}
///
/// Gets the colors.
///
- protected Buffer Colors { get; }
+ protected IBuffer Colors { get; }
///
/// Gets the color for a single pixel.
@@ -81,7 +78,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
///
/// The color
///
- internal override TPixel this[int x, int y] => this.Colors[x];
+ internal override TPixel this[int x, int y] => this.Colors.Span[x];
///
public override void Dispose()
@@ -92,23 +89,20 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
///
internal override void Apply(Span scanline, int x, int y)
{
- try
+ Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
+
+ MemoryManager memoryManager = this.Target.MemoryManager;
+
+ using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length))
{
- Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
+ Span amountSpan = amountBuffer.Span;
- using (var amountBuffer = new Buffer(scanline.Length))
+ for (int i = 0; i < scanline.Length; i++)
{
- for (int i = 0; i < scanline.Length; i++)
- {
- amountBuffer[i] = scanline[i] * this.Options.BlendPercentage;
- }
-
- this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer);
+ amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
- }
- catch (Exception)
- {
- throw;
+
+ this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan);
}
}
}
diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs
index d55e22416..f1db72db6 100644
--- a/src/ImageSharp.Drawing/DrawImage.cs
+++ b/src/ImageSharp.Drawing/DrawImage.cs
@@ -18,24 +18,13 @@ namespace SixLabors.ImageSharp
/// The image this method extends.
/// The image to blend with the currently processing image.
/// The pixel format.
- /// The size to draw the blended image.
/// The location to draw the blended image.
/// The options.
/// The .
- public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Size size, Point location, GraphicsOptions options)
+ public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options)
where TPixel : struct, IPixel
{
- if (size == default(Size))
- {
- size = new Size(image.Width, image.Height);
- }
-
- if (location == default(Point))
- {
- location = Point.Empty;
- }
-
- source.ApplyProcessor(new DrawImageProcessor(image, size, location, options));
+ source.ApplyProcessor(new DrawImageProcessor(image, location, options));
return source;
}
@@ -45,14 +34,14 @@ namespace SixLabors.ImageSharp
/// The pixel format.
/// The image this method extends.
/// The image to blend with the currently processing image.
- /// The opacity of the image image to blend. Must be between 0 and 1.
+ /// The opacity of the image to blend. Must be between 0 and 1.
/// The .
- public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float percent)
+ public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float opacity)
where TPixel : struct, IPixel
{
GraphicsOptions options = GraphicsOptions.Default;
- options.BlendPercentage = percent;
- return DrawImage(source, image, default(Size), default(Point), options);
+ options.BlendPercentage = opacity;
+ return DrawImage(source, image, Point.Empty, options);
}
///
@@ -62,15 +51,15 @@ namespace SixLabors.ImageSharp
/// The image this method extends.
/// The image to blend with the currently processing image.
/// The blending mode.
- /// The opacity of the image image to blend. Must be between 0 and 1.
+ /// The opacity of the image to blend. Must be between 0 and 1.
/// The .
- public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent)
+ public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float opacity)
where TPixel : struct, IPixel
{
GraphicsOptions options = GraphicsOptions.Default;
- options.BlendPercentage = percent;
+ options.BlendPercentage = opacity;
options.BlenderMode = blender;
- return DrawImage(source, image, default(Size), default(Point), options);
+ return DrawImage(source, image, Point.Empty, options);
}
///
@@ -79,12 +68,12 @@ namespace SixLabors.ImageSharp
/// The pixel format.
/// The image this method extends.
/// The image to blend with the currently processing image.
- /// The options, including the blending type and belnding amount.
+ /// The options, including the blending type and blending amount.
/// The .
public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, GraphicsOptions options)
where TPixel : struct, IPixel
{
- return DrawImage(source, image, default(Size), default(Point), options);
+ return DrawImage(source, image, Point.Empty, options);
}
///
@@ -93,16 +82,15 @@ namespace SixLabors.ImageSharp
/// The image this method extends.
/// The image to blend with the currently processing image.
/// The pixel format.
- /// The opacity of the image image to blend. Must be between 0 and 1.
- /// The size to draw the blended image.
+ /// The opacity of the image to blend. Must be between 0 and 1.
/// The location to draw the blended image.
/// The .
- public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float percent, Size size, Point location)
+ public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity, Point location)
where TPixel : struct, IPixel
{
GraphicsOptions options = GraphicsOptions.Default;
- options.BlendPercentage = percent;
- return source.DrawImage(image, size, location, options);
+ options.BlendPercentage = opacity;
+ return source.DrawImage(image, location, options);
}
///
@@ -112,17 +100,16 @@ namespace SixLabors.ImageSharp
/// The image to blend with the currently processing image.
/// The pixel format.
/// The type of bending to apply.
- /// The opacity of the image image to blend. Must be between 0 and 1.
- /// The size to draw the blended image.
+ /// The opacity of the image to blend. Must be between 0 and 1.
/// The location to draw the blended image.
/// The .
- public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent, Size size, Point location)
+ public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float opacity, Point location)
where TPixel : struct, IPixel
{
GraphicsOptions options = GraphicsOptions.Default;
options.BlenderMode = blender;
- options.BlendPercentage = percent;
- return source.DrawImage(image, size, location, options);
+ options.BlendPercentage = opacity;
+ return source.DrawImage(image, location, options);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index eb3c29dd9..3e320dccc 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -36,9 +36,9 @@
-
-
-
+
+
+
All
diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs
index 61f1291c4..4c2278719 100644
--- a/src/ImageSharp.Drawing/Paths/ShapePath.cs
+++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs
@@ -4,6 +4,8 @@
using System;
using System.Buffers;
using System.Numerics;
+
+using SixLabors.ImageSharp.Memory;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Drawing
diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs
index a96b03dd0..cc27f7fbb 100644
--- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs
+++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs
@@ -46,18 +46,17 @@ namespace SixLabors.ImageSharp.Drawing
{
var start = new PointF(this.Bounds.Left - 1, y);
var end = new PointF(this.Bounds.Right + 1, y);
- using (var innerBuffer = new Buffer(buffer.Length))
- {
- PointF[] array = innerBuffer.Array;
- int count = this.Shape.FindIntersections(start, end, array, 0);
- for (int i = 0; i < count; i++)
- {
- buffer[i + offset] = array[i].X;
- }
+ // TODO: This is a temporary workaround because of the lack of Span API-s on IPath. We should use MemoryManager.Allocate() here!
+ PointF[] innerBuffer = new PointF[buffer.Length];
+ int count = this.Shape.FindIntersections(start, end, innerBuffer, 0);
- return count;
+ for (int i = 0; i < count; i++)
+ {
+ buffer[i + offset] = innerBuffer[i].X;
}
+
+ return count;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
index 47763c0aa..632b4d449 100644
--- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs
@@ -19,93 +19,77 @@ namespace SixLabors.ImageSharp.Drawing.Processors
internal class DrawImageProcessor : ImageProcessor
where TPixel : struct, IPixel
{
- private readonly PixelBlender blender;
-
///
/// Initializes a new instance of the class.
///
/// The image to blend with the currently processing image.
- /// The size to draw the blended image.
/// The location to draw the blended image.
- /// The opacity of the image to blend. Between 0 and 100.
- public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options)
+ /// The opacity of the image to blend. Between 0 and 1.
+ public DrawImageProcessor(Image image, Point location, GraphicsOptions options)
{
Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage));
+
this.Image = image;
- this.Size = size;
- this.Alpha = options.BlendPercentage;
- this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode);
+ this.Opacity = options.BlendPercentage;
+ this.Blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode);
this.Location = location;
}
///
- /// Gets the image to blend.
+ /// Gets the image to blend
///
public Image Image { get; }
///
- /// Gets the alpha percentage value.
+ /// Gets the opacity of the image to blend
///
- public float Alpha { get; }
+ public float Opacity { get; }
///
- /// Gets the size to draw the blended image.
+ /// Gets the pixel blender
///
- public Size Size { get; }
+ public PixelBlender Blender { get; }
///
- /// Gets the location to draw the blended image.
+ /// Gets the location to draw the blended image
///
public Point Location { get; }
///
protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
- Image disposableImage = null;
Image targetImage = this.Image;
+ PixelBlender blender = this.Blender;
+ int locationY = this.Location.Y;
- try
- {
- if (targetImage.Size() != this.Size)
- {
- targetImage = disposableImage = this.Image.Clone(x => x.Resize(this.Size.Width, this.Size.Height));
- }
+ // Align start/end positions.
+ Rectangle bounds = targetImage.Bounds();
- // Align start/end positions.
- Rectangle bounds = targetImage.Bounds();
- int minX = Math.Max(this.Location.X, sourceRectangle.X);
- int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
- maxX = Math.Min(this.Location.X + this.Size.Width, maxX);
- int targetX = minX - this.Location.X;
+ int minX = Math.Max(this.Location.X, sourceRectangle.X);
+ int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
+ int targetX = minX - this.Location.X;
- int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
- int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
+ int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
+ int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);
- maxY = Math.Min(this.Location.Y + this.Size.Height, maxY);
+ int width = maxX - minX;
- int width = maxX - minX;
- using (var amount = new Buffer(width))
- {
- for (int i = 0; i < width; i++)
- {
- amount[i] = this.Alpha;
- }
+ MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager;
- Parallel.For(
- minY,
- maxY,
- configuration.ParallelOptions,
- y =>
- {
- Span background = source.GetPixelRowSpan(y).Slice(minX, width);
- Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width);
- this.blender.Blend(background, background, foreground, amount);
- });
- }
- }
- finally
+ using (IBuffer amount = memoryManager.Allocate(width))
{
- disposableImage?.Dispose();
+ amount.Span.Fill(this.Opacity);
+
+ Parallel.For(
+ minY,
+ maxY,
+ configuration.ParallelOptions,
+ y =>
+ {
+ Span background = source.GetPixelRowSpan(y).Slice(minX, width);
+ Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
+ blender.Blend(memoryManager, background, background, foreground, amount.Span);
+ });
}
}
}
diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs
index 679ca6a22..3bf18a37b 100644
--- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs
@@ -66,25 +66,25 @@ namespace SixLabors.ImageSharp.Drawing.Processors
int width = maxX - minX;
- using (var amount = new Buffer(width))
- using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
+ using (IBuffer amount = source.MemoryManager.Allocate(width))
+ using (BrushApplicator applicator = this.brush.CreateApplicator(
+ source,
+ sourceRectangle,
+ this.options))
{
- for (int i = 0; i < width; i++)
- {
- amount[i] = this.options.BlendPercentage;
- }
+ amount.Span.Fill(this.options.BlendPercentage);
- Parallel.For(
+ Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
- {
- int offsetY = y - startY;
- int offsetX = minX - startX;
+ {
+ int offsetY = y - startY;
+ int offsetX = minX - startX;
- applicator.Apply(amount, offsetX, offsetY);
- });
+ applicator.Apply(amount.Span, offsetX, offsetY);
+ });
}
}
}
diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
index b6ef4be21..076785526 100644
--- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
@@ -78,8 +78,6 @@ namespace SixLabors.ImageSharp.Drawing.Processors
return; // no effect inside image;
}
- ArrayPool arrayPool = ArrayPool.Shared;
-
int maxIntersections = region.MaxIntersections;
float subpixelCount = 4;
@@ -100,101 +98,94 @@ namespace SixLabors.ImageSharp.Drawing.Processors
using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
- float[] buffer = arrayPool.Rent(maxIntersections);
int scanlineWidth = maxX - minX;
- using (var scanline = new Buffer(scanlineWidth))
+ using (BasicArrayBuffer buffer = source.MemoryManager.AllocateFake(maxIntersections))
+ using (BasicArrayBuffer scanline = source.MemoryManager.AllocateFake(scanlineWidth))
{
- try
+ bool scanlineDirty = true;
+ for (int y = minY; y < maxY; y++)
{
- bool scanlineDirty = true;
- for (int y = minY; y < maxY; y++)
+ if (scanlineDirty)
{
- if (scanlineDirty)
+ // clear the buffer
+ for (int x = 0; x < scanlineWidth; x++)
{
- // clear the buffer
- for (int x = 0; x < scanlineWidth; x++)
- {
- scanline[x] = 0;
- }
-
- scanlineDirty = false;
+ scanline[x] = 0;
}
- float subpixelFraction = 1f / subpixelCount;
- float subpixelFractionPoint = subpixelFraction / subpixelCount;
- for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
- {
- int pointsFound = region.Scan(subPixel + offset, buffer, 0);
- if (pointsFound == 0)
- {
- // nothing on this line skip
- continue;
- }
+ scanlineDirty = false;
+ }
- QuickSort(new Span(buffer, 0, pointsFound));
+ float subpixelFraction = 1f / subpixelCount;
+ float subpixelFractionPoint = subpixelFraction / subpixelCount;
+ for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
+ {
+ int pointsFound = region.Scan(subPixel + offset, buffer.Array, 0);
+ if (pointsFound == 0)
+ {
+ // nothing on this line skip
+ continue;
+ }
- for (int point = 0; point < pointsFound; point += 2)
- {
- // points will be paired up
- float scanStart = buffer[point] - minX;
- float scanEnd = buffer[point + 1] - minX;
- int startX = (int)MathF.Floor(scanStart + offset);
- int endX = (int)MathF.Floor(scanEnd + offset);
+ QuickSort(new Span(buffer.Array, 0, pointsFound));
- if (startX >= 0 && startX < scanline.Length)
- {
- for (float x = scanStart; x < startX + 1; x += subpixelFraction)
- {
- scanline[startX] += subpixelFractionPoint;
- scanlineDirty = true;
- }
- }
+ for (int point = 0; point < pointsFound; point += 2)
+ {
+ // points will be paired up
+ float scanStart = buffer[point] - minX;
+ float scanEnd = buffer[point + 1] - minX;
+ int startX = (int)MathF.Floor(scanStart + offset);
+ int endX = (int)MathF.Floor(scanEnd + offset);
- if (endX >= 0 && endX < scanline.Length)
+ if (startX >= 0 && startX < scanline.Length)
+ {
+ for (float x = scanStart; x < startX + 1; x += subpixelFraction)
{
- for (float x = endX; x < scanEnd; x += subpixelFraction)
- {
- scanline[endX] += subpixelFractionPoint;
- scanlineDirty = true;
- }
+ scanline[startX] += subpixelFractionPoint;
+ scanlineDirty = true;
}
+ }
- int nextX = startX + 1;
- endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
- nextX = Math.Max(nextX, 0);
- for (int x = nextX; x < endX; x++)
+ if (endX >= 0 && endX < scanline.Length)
+ {
+ for (float x = endX; x < scanEnd; x += subpixelFraction)
{
- scanline[x] += subpixelFraction;
+ scanline[endX] += subpixelFractionPoint;
scanlineDirty = true;
}
}
+
+ int nextX = startX + 1;
+ endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
+ nextX = Math.Max(nextX, 0);
+ for (int x = nextX; x < endX; x++)
+ {
+ scanline[x] += subpixelFraction;
+ scanlineDirty = true;
+ }
}
+ }
- if (scanlineDirty)
+ if (scanlineDirty)
+ {
+ if (!this.Options.Antialias)
{
- if (!this.Options.Antialias)
+ for (int x = 0; x < scanlineWidth; x++)
{
- for (int x = 0; x < scanlineWidth; x++)
+ if (scanline[x] >= 0.5)
+ {
+ scanline[x] = 1;
+ }
+ else
{
- if (scanline[x] >= 0.5)
- {
- scanline[x] = 1;
- }
- else
- {
- scanline[x] = 0;
- }
+ scanline[x] = 0;
}
}
-
- applicator.Apply(scanline, minX, y);
}
+
+ applicator.Apply(scanline.Span, minX, y);
}
}
- finally
- {
- arrayPool.Return(buffer);
- }
}
}
}
diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index 0acb846c5..24d2dd4cc 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -88,6 +89,14 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : struct, IPixel
=> source.Frames.RootFrame.GetPixelRowSpan(row);
+ ///
+ /// Gets the assigned to 'source'.
+ ///
+ /// The source image
+ /// Returns the configuration.
+ internal static MemoryManager GetMemoryManager(this IConfigurable source)
+ => GetConfiguration(source).MemoryManager;
+
///
/// Gets the span to the backing buffer.
///
@@ -140,6 +149,6 @@ namespace SixLabors.ImageSharp.Advanced
/// A reference to the element.
private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(IPixelSource source)
where TPixel : struct, IPixel
- => ref source.PixelBuffer.Span.DangerousGetPinnableReference();
+ => ref MemoryMarshal.GetReference(source.PixelBuffer.Span);
}
}
diff --git a/src/ImageSharp/ApplyProcessors.cs b/src/ImageSharp/ApplyProcessors.cs
index 58a952c40..c4954ef0d 100644
--- a/src/ImageSharp/ApplyProcessors.cs
+++ b/src/ImageSharp/ApplyProcessors.cs
@@ -4,7 +4,6 @@
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
namespace SixLabors.ImageSharp
{
diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs
index 107be4cb2..cb08d08bf 100644
--- a/src/ImageSharp/ColorSpaces/CieLab.cs
+++ b/src/ImageSharp/ColorSpaces/CieLab.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents a CIE L*a*b* 1976 color.
///
///
- internal struct CieLab : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct CieLab : IColorVector, IEquatable, IAlmostEquatable
{
///
/// D50 standard illuminant.
diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs
index 834ef56a8..94443fd86 100644
--- a/src/ImageSharp/ColorSpaces/CieLch.cs
+++ b/src/ImageSharp/ColorSpaces/CieLch.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color.
///
///
- internal struct CieLch : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct CieLch : IColorVector, IEquatable, IAlmostEquatable
{
///
/// D50 standard illuminant.
diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs
index f35914d64..705b770d3 100644
--- a/src/ImageSharp/ColorSpaces/CieLchuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color.
///
///
- internal struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable
{
///
/// D50 standard illuminant.
diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs
index 9b5251708..b0ae048ab 100644
--- a/src/ImageSharp/ColorSpaces/CieLuv.cs
+++ b/src/ImageSharp/ColorSpaces/CieLuv.cs
@@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// attempted perceptual uniformity
///
///
- internal struct CieLuv : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct CieLuv : IColorVector, IEquatable, IAlmostEquatable
{
///
/// D65 standard illuminant.
diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
index d9767d45e..d0a70dd19 100644
--- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
@@ -6,12 +6,13 @@ using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
+// ReSharper disable CompareOfFloatsByEqualityOperator
namespace SixLabors.ImageSharp.ColorSpaces
{
///
/// Represents the coordinates of CIEXY chromaticity space
///
- internal struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable
+ internal readonly struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable
{
///
/// Represents a that has X, Y values set to zero.
@@ -143,7 +144,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CieXyChromaticityCoordinates other)
{
- return this.backingVector.Equals(other.backingVector);
+ // The memberwise comparison here is a workaround for https://github.com/dotnet/coreclr/issues/16443
+ return this.X == other.X && this.Y == other.Y;
}
///
diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs
index d5ef4b15d..751830a0b 100644
--- a/src/ImageSharp/ColorSpaces/CieXyy.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyy.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE xyY 1931 color
///
///
- internal struct CieXyy : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct CieXyy : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has X, Y, and Y values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs
index 908408000..0f1866009 100644
--- a/src/ImageSharp/ColorSpaces/CieXyz.cs
+++ b/src/ImageSharp/ColorSpaces/CieXyz.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an CIE XYZ 1931 color
///
///
- internal struct CieXyz : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct CieXyz : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has X, Y, and Z values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs
index 2a58a5762..2eb148a8c 100644
--- a/src/ImageSharp/ColorSpaces/Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Cmyk.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
///
- internal struct Cmyk : IEquatable, IAlmostEquatable
+ internal readonly struct Cmyk : IEquatable, IAlmostEquatable
{
///
/// Represents a that has C, M, Y, and K values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
index de13b97eb..6844e3a3c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
@@ -184,8 +184,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
Rgb rgb = YCbCrAndRgbConverter.Convert(color);
// Adaptation
- // TODO: Check this!
- return rgb.WorkingSpace.Equals(this.TargetRgbWorkingSpace) ? rgb : this.Adapt(rgb);
+ return this.Adapt(rgb);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs
index 2ec79b353..b40a02af7 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
internal abstract class LinearRgbAndCieXyzConverterBase
{
///
- /// Geturns the correct matrix to convert between the Rgb and CieXyz color space.
+ /// Returns the correct matrix to convert between the Rgb and CieXyz color space.
///
/// The Rgb working space.
/// The based on the chromaticity and working space.
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix);
- // Use transposed Rows/Coloumns
+ // Use transposed Rows/Columns
// TODO: Is there a built in method for this multiplication?
return new Matrix4x4
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs
index 19d413037..bf36e252a 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs
@@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
public CieXyz Convert(LinearRgb input)
{
DebugGuard.NotNull(input, nameof(input));
- Guard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
+ DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix);
return new CieXyz(vector);
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
index 8a2c66a80..5a5c39647 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
///
/// Trivial implementation of
///
- internal struct RgbWorkingSpace : IRgbWorkingSpace
+ internal readonly struct RgbWorkingSpace : IRgbWorkingSpace
{
///
/// Initializes a new instance of the struct.
diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs
index cf880f154..1944ac0c6 100644
--- a/src/ImageSharp/ColorSpaces/Hsl.cs
+++ b/src/ImageSharp/ColorSpaces/Hsl.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
/// Represents a Hsl (hue, saturation, lightness) color.
///
- internal struct Hsl : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct Hsl : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has H, S, and L values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs
index 9f4739379..45ffd7f12 100644
--- a/src/ImageSharp/ColorSpaces/Hsv.cs
+++ b/src/ImageSharp/ColorSpaces/Hsv.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
///
- internal struct Hsv : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct Hsv : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has H, S, and V values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs
index b5ba7c86c..de42518d7 100644
--- a/src/ImageSharp/ColorSpaces/HunterLab.cs
+++ b/src/ImageSharp/ColorSpaces/HunterLab.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// Represents an Hunter LAB color.
///
///
- internal struct HunterLab : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct HunterLab : IColorVector, IEquatable, IAlmostEquatable
{
///
/// D50 standard illuminant.
diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs
index 07889c352..b8c446285 100644
--- a/src/ImageSharp/ColorSpaces/LinearRgb.cs
+++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
/// Represents an linear Rgb color with specified working space
///
- internal struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has R, G, and B values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs
index 82c291de3..72ac16f21 100644
--- a/src/ImageSharp/ColorSpaces/Lms.cs
+++ b/src/ImageSharp/ColorSpaces/Lms.cs
@@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// named after their responsivity (sensitivity) at long, medium and short wavelengths.
///
///
- internal struct Lms : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct Lms : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has L, M, and S values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs
index 8ac8411b2..53fa6086d 100644
--- a/src/ImageSharp/ColorSpaces/Rgb.cs
+++ b/src/ImageSharp/ColorSpaces/Rgb.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
/// Represents an RGB color with specified working space
///
- internal struct Rgb : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct Rgb : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has R, G, and B values set to zero.
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
index 708a74308..44a0b245d 100644
--- a/src/ImageSharp/ColorSpaces/YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/YCbCr.cs
@@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
///
///
///
- internal struct YCbCr : IColorVector, IEquatable, IAlmostEquatable
+ internal readonly struct YCbCr : IColorVector, IEquatable, IAlmostEquatable
{
///
/// Represents a that has Y, Cb, and Cr values set to zero.
diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
index 8bebb3de7..d6dade770 100644
--- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
@@ -169,19 +169,5 @@ namespace SixLabors.ImageSharp
{
return (byte)value.Clamp(0, 255);
}
-
- ///
- /// Swaps the references to two objects in memory.
- ///
- /// The first reference.
- /// The second reference.
- /// The type of object.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Swap(ref T first, ref T second)
- {
- T temp = second;
- second = first;
- first = temp;
- }
}
}
diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs
index 0188bc03c..7b77fefca 100644
--- a/src/ImageSharp/Common/Extensions/SimdUtils.cs
+++ b/src/ImageSharp/Common/Extensions/SimdUtils.cs
@@ -76,14 +76,14 @@ namespace SixLabors.ImageSharp
return;
}
- ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference());
- ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference());
+ ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
+ ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector magick = new Vector(32768.0f);
Vector scale = new Vector(255f) / new Vector(256f);
- // need to copy to a temporal struct, because
+ // need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
@@ -117,14 +117,14 @@ namespace SixLabors.ImageSharp
return;
}
- ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference());
- ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference());
+ ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
+ ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector magick = new Vector(32768.0f);
Vector scale = new Vector(255f) / new Vector(256f);
- // need to copy to a temporal struct, because
+ // need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x)
// does not work. TODO: This might be a CoreClr bug, need to ask/report
var temp = default(Octet.OfUInt32);
diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index b717abab1..7a9a34ac1 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -29,23 +29,16 @@ namespace SixLabors.ImageSharp
}
else
{
- byte[] foo = ArrayPool.Shared.Rent(count);
- try
+ byte[] foo = new byte[count];
+ while (count > 0)
{
- while (count > 0)
+ int bytesRead = stream.Read(foo, 0, count);
+ if (bytesRead == 0)
{
- int bytesRead = stream.Read(foo, 0, count);
- if (bytesRead == 0)
- {
- break;
- }
-
- count -= bytesRead;
+ break;
}
- }
- finally
- {
- ArrayPool.Shared.Return(foo);
+
+ count -= bytesRead;
}
}
}
diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
index 7cb193e82..8133ebb38 100644
--- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
+++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs
@@ -12,6 +12,34 @@ namespace SixLabors.ImageSharp
///
internal static class Vector4Extensions
{
+ ///
+ /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
+ ///
+ /// The to premultiply
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 Premultiply(this Vector4 source)
+ {
+ float w = source.W;
+ Vector4 premultiplied = source * w;
+ premultiplied.W = w;
+ return premultiplied;
+ }
+
+ ///
+ /// Reverses the result of premultiplying a vector via .
+ ///
+ /// The to premultiply
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 UnPremultiply(this Vector4 source)
+ {
+ float w = source.W;
+ Vector4 unpremultiplied = source / w;
+ unpremultiplied.W = w;
+ return unpremultiplied;
+ }
+
///
/// Compresses a linear color signal to its sRGB equivalent.
///
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index 7d781e77f..75c9190d2 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -139,27 +139,6 @@ namespace SixLabors.ImageSharp
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
- ///
- /// Gets the bounding from the given matrix.
- ///
- /// The source rectangle.
- /// The transformation matrix.
- ///
- /// The .
- ///
- public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix)
- {
- var leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix);
- var rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix);
- var leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix);
- var rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
-
- Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom };
- float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min();
- float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min();
- return new Rectangle(0, 0, (int)extentX, (int)extentY);
- }
-
///
/// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one.
diff --git a/src/ImageSharp/Common/Helpers/ParallelFor.cs b/src/ImageSharp/Common/Helpers/ParallelFor.cs
new file mode 100644
index 000000000..da9125905
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/ParallelFor.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Utility methods for Parallel.For() execution. Use this instead of raw calls!
+ ///
+ internal static class ParallelFor
+ {
+ ///
+ /// Helper method to execute Parallel.For using the settings in
+ ///
+ public static void WithConfiguration(int fromInclusive, int toExclusive, Configuration configuration, Action body)
+ {
+ Parallel.For(fromInclusive, toExclusive, configuration.ParallelOptions, body);
+ }
+
+ ///
+ /// Helper method to execute Parallel.For with temporary worker buffer shared between executing tasks.
+ /// The buffer is not guaranteed to be clean!
+ ///
+ /// The value type of the buffer
+ /// The start index, inclusive.
+ /// The end index, exclusive.
+ /// The used for getting the and
+ /// The length of the requested parallel buffer
+ /// The delegate that is invoked once per iteration.
+ public static void WithTemporaryBuffer(
+ int fromInclusive,
+ int toExclusive,
+ Configuration configuration,
+ int bufferLength,
+ Action> body)
+ where T : struct
+ {
+ MemoryManager memoryManager = configuration.MemoryManager;
+ ParallelOptions parallelOptions = configuration.ParallelOptions;
+
+ IBuffer InitBuffer()
+ {
+ return memoryManager.Allocate(bufferLength);
+ }
+
+ void CleanUpBuffer(IBuffer buffer)
+ {
+ buffer.Dispose();
+ }
+
+ IBuffer BodyFunc(int i, ParallelLoopState state, IBuffer buffer)
+ {
+ body(i, buffer);
+ return buffer;
+ }
+
+ Parallel.For(fromInclusive, toExclusive, parallelOptions, InitBuffer, BodyFunc, CleanUpBuffer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index 740103533..d41e48678 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -1,261 +1,145 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.Formats.Bmp;
-using SixLabors.ImageSharp.Formats.Gif;
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.IO;
-
-namespace SixLabors.ImageSharp
-{
- ///
- /// Provides initialization code which allows extending the library.
- ///
- public sealed class Configuration
- {
- ///
- /// A lazily initialized configuration default instance.
- ///
- private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance);
-
- ///
- /// The list of supported keyed to mime types.
- ///
- private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary();
-
- ///
- /// The list of supported keyed to mime types.
- ///
- private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary();
-
- ///
- /// The list of supported s.
- ///
- private readonly ConcurrentBag imageFormats = new ConcurrentBag();
-
- ///
- /// The list of supported s.
- ///
- private ConcurrentBag imageFormatDetectors = new ConcurrentBag();
-
- ///
- /// Initializes a new instance of the class.
- ///
- public Configuration()
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// A collection of configuration modules to register
- public Configuration(params IConfigurationModule[] configurationModules)
- {
- if (configurationModules != null)
- {
- foreach (IConfigurationModule p in configurationModules)
- {
- p.Configure(this);
- }
- }
- }
-
- ///
- /// Gets the default instance.
- ///
- public static Configuration Default { get; } = Lazy.Value;
-
- ///
- /// Gets the global parallel options for processing tasks in parallel.
- ///
- public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
-
- ///
- /// Gets the currently registered s.
- ///
- public IEnumerable ImageFormats => this.imageFormats;
-
- ///
- /// Gets the maximum header size of all the formats.
- ///
- internal int MaxHeaderSize { get; private set; }
-
- ///
- /// Gets the currently registered s.
- ///
- internal IEnumerable FormatDetectors => this.imageFormatDetectors;
-
- ///
- /// Gets the currently registered s.
- ///
- internal IEnumerable> ImageDecoders => this.mimeTypeDecoders;
-
- ///
- /// Gets the currently registered s.
- ///
- internal IEnumerable> ImageEncoders => this.mimeTypeEncoders;
-
-#if !NETSTANDARD1_1
- ///
- /// Gets or sets the filesystem helper for accessing the local file system.
- ///
- internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
-#endif
-
- ///
- /// Gets or sets the image operations provider factory.
- ///
- internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
-
- ///
- /// Registers a new format provider.
- ///
- /// The configuration provider to call configure on.
- public void Configure(IConfigurationModule configuration)
- {
- Guard.NotNull(configuration, nameof(configuration));
- configuration.Configure(this);
- }
-
- ///
- /// Registers a new format provider.
- ///
- /// The format to register as a known format.
- public void AddImageFormat(IImageFormat format)
- {
- Guard.NotNull(format, nameof(format));
- Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
- Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
- this.imageFormats.Add(format);
- }
-
- ///
- /// For the specified file extensions type find the e .
- ///
- /// The extension to discover
- /// The if found otherwise null
- public IImageFormat FindFormatByFileExtension(string extension)
- {
- return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
- }
-
- ///
- /// For the specified mime type find the .
- ///
- /// The mime-type to discover
- /// The if found; otherwise null
- public IImageFormat FindFormatByMimeType(string mimeType)
- {
- return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
- }
-
- ///
- /// Sets a specific image encoder as the encoder for a specific image format.
- ///
- /// The image format to register the encoder for.
- /// The encoder to use,
- public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
- {
- Guard.NotNull(imageFormat, nameof(imageFormat));
- Guard.NotNull(encoder, nameof(encoder));
- this.AddImageFormat(imageFormat);
- this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
- }
-
- ///
- /// Sets a specific image decoder as the decoder for a specific image format.
- ///
- /// The image format to register the encoder for.
- /// The decoder to use,
- 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);
- }
-
- ///
- /// Removes all the registered image format detectors.
- ///
- public void ClearImageFormatDetectors()
- {
- this.imageFormatDetectors = new ConcurrentBag();
- }
-
- ///
- /// Adds a new detector for detecting mime types.
- ///
- /// The detector to add
- public void AddImageFormatDetector(IImageFormatDetector detector)
- {
- Guard.NotNull(detector, nameof(detector));
- this.imageFormatDetectors.Add(detector);
- this.SetMaxHeaderSize();
- }
-
- ///
- /// For the specified mime type find the decoder.
- ///
- /// The format to discover
- /// The if found otherwise null
- public IImageDecoder FindDecoder(IImageFormat format)
- {
- Guard.NotNull(format, nameof(format));
- if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
- {
- return decoder;
- }
-
- return null;
- }
-
- ///
- /// For the specified mime type find the encoder.
- ///
- /// The format to discover
- /// The if found otherwise null
- public IImageEncoder FindEncoder(IImageFormat format)
- {
- Guard.NotNull(format, nameof(format));
- if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
- {
- return encoder;
- }
-
- return null;
- }
-
- ///
- /// Creates the default instance with the following s preregistered:
- ///
- ///
- ///
- ///
- ///
- /// The default configuration of
- internal static Configuration CreateDefaultInstance()
- {
- return new Configuration(
- new PngConfigurationModule(),
- new JpegConfigurationModule(),
- new GifConfigurationModule(),
- new BmpConfigurationModule());
- }
-
- ///
- /// Sets the max header size.
- ///
- private void SetMaxHeaderSize()
- {
- this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
- }
- }
-}
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.Formats.Bmp;
+using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Provides initialization code which allows extending the library.
+ ///
+ public sealed class Configuration
+ {
+ ///
+ /// A lazily initialized configuration default instance.
+ ///
+ private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Configuration()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A collection of configuration modules to register
+ public Configuration(params IConfigurationModule[] configurationModules)
+ {
+ if (configurationModules != null)
+ {
+ foreach (IConfigurationModule p in configurationModules)
+ {
+ p.Configure(this);
+ }
+ }
+ }
+
+ ///
+ /// Gets the default instance.
+ ///
+ public static Configuration Default { get; } = Lazy.Value;
+
+ ///
+ /// Gets the global parallel options for processing tasks in parallel.
+ ///
+ public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
+
+ ///
+ /// Gets the currently registered s.
+ ///
+ public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats;
+
+ ///
+ /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source.
+ ///
+ public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current;
+
+ ///
+ /// Gets or sets the that is currently in use.
+ ///
+ public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();
+
+ ///
+ /// Gets or sets the that is currently in use.
+ ///
+ public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault();
+
+ ///
+ /// Gets the maximum header size of all the formats.
+ ///
+ internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize;
+
+#if !NETSTANDARD1_1
+ ///
+ /// Gets or sets the filesystem helper for accessing the local file system.
+ ///
+ internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
+#endif
+
+ ///
+ /// Gets or sets the image operations provider factory.
+ ///
+ internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
+
+ ///
+ /// Registers a new format provider.
+ ///
+ /// The configuration provider to call configure on.
+ public void Configure(IConfigurationModule configuration)
+ {
+ Guard.NotNull(configuration, nameof(configuration));
+ configuration.Configure(this);
+ }
+
+ ///
+ /// Creates a shallow copy of the
+ ///
+ /// A new configuration instance
+ public Configuration ShallowCopy()
+ {
+ return new Configuration
+ {
+ ParallelOptions = this.ParallelOptions,
+ ImageFormatsManager = this.ImageFormatsManager,
+ MemoryManager = this.MemoryManager,
+ ImageOperationsProvider = this.ImageOperationsProvider,
+ ReadOrigin = this.ReadOrigin,
+
+#if !NETSTANDARD1_1
+ FileSystem = this.FileSystem
+#endif
+ };
+ }
+
+ ///
+ /// Creates the default instance with the following s preregistered:
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The default configuration of
+ internal static Configuration CreateDefaultInstance()
+ {
+ return new Configuration(
+ new PngConfigurationModule(),
+ new JpegConfigurationModule(),
+ new GifConfigurationModule(),
+ new BmpConfigurationModule());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs
index 575525a77..7ccc65e27 100644
--- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs
+++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs
@@ -1,7 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
+using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
@@ -34,6 +36,9 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ public MemoryManager MemoryManager => this.source.GetConfiguration().MemoryManager;
+
///
public Image Apply()
{
@@ -46,14 +51,17 @@ namespace SixLabors.ImageSharp
return this.destination;
}
+ ///
+ public Size GetCurrentSize() => this.GetCurrentBounds().Size;
+
///
public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle)
{
if (!this.mutate && this.destination == null)
{
// This will only work if the first processor applied is the cloning one thus
- // realistically for this optermissation to work the resize must the first processor
- // applied any only up processors will take the douple data path.
+ // realistically for this optimization to work the resize must the first processor
+ // applied any only up processors will take the double data path.
if (processor is ICloningImageProcessor cloningImageProcessor)
{
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle);
@@ -70,7 +78,12 @@ namespace SixLabors.ImageSharp
///
public IImageProcessingContext ApplyProcessor(IImageProcessor processor)
{
- return this.ApplyProcessor(processor, this.source.Bounds());
+ return this.ApplyProcessor(processor, this.GetCurrentBounds());
+ }
+
+ private Rectangle GetCurrentBounds()
+ {
+ return this.destination?.Bounds() ?? this.source.Bounds();
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
index 510a097ea..8f448198b 100644
--- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
@@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Dithering.Base
this.startingOffset = 0;
for (int i = 0; i < this.matrixWidth; i++)
{
- // Good to disable here as we are not comparing matematical output.
+ // Good to disable here as we are not comparing mathematical output.
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (matrix[0, i] != 0)
{
@@ -70,27 +70,15 @@ namespace SixLabors.ImageSharp.Dithering.Base
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Dither(ImageFrame pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
+ public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
where TPixel : struct, IPixel
{
- this.Dither(pixels, source, transformed, x, y, minX, minY, maxX, maxY, true);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
- where TPixel : struct, IPixel
- {
- if (replacePixel)
- {
- // Assign the transformed pixel to the array.
- image[x, y] = transformed;
- }
+ image[x, y] = transformed;
// Calculate the error
Vector4 error = source.ToVector4() - transformed.ToVector4();
- // Loop through and distribute the error amongst neighbouring pixels.
+ // Loop through and distribute the error amongst neighboring pixels.
for (int row = 0; row < this.matrixHeight; row++)
{
int matrixY = y + row;
@@ -115,10 +103,8 @@ namespace SixLabors.ImageSharp.Dithering.Base
ref TPixel pixel = ref rowSpan[matrixX];
var offsetColor = pixel.ToVector4();
- var coefficientVector = new Vector4(coefficient);
- Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor;
- result.W = offsetColor.W;
+ Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor;
pixel.PackFromVector4(result);
}
}
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
index c538d643c..dabc4e682 100644
--- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs
@@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering
{
///
- /// Encapsulates properties and methods required to perfom diffused error dithering on an image.
+ /// Encapsulates properties and methods required to perform diffused error dithering on an image.
///
public interface IErrorDiffuser
{
@@ -25,25 +25,5 @@ namespace SixLabors.ImageSharp.Dithering
/// The pixel format.
void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY)
where TPixel : struct, IPixel;
-
- ///
- /// Transforms the image applying the dither matrix. This method alters the input pixels array
- ///
- /// The image
- /// The source pixel
- /// The transformed pixel
- /// The column index.
- /// The row index.
- /// The minimum column value.
- /// The minimum row value.
- /// The maximum column value.
- /// The maximum row value.
- ///
- /// Whether to replace the pixel at the given coordinates with the transformed value.
- /// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false.
- ///
- /// The pixel format.
- void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel)
- where TPixel : struct, IPixel;
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs b/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs
new file mode 100644
index 000000000..c75530b8e
--- /dev/null
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Contains reusable static instances of known error diffusion algorithms
+ ///
+ public static class KnownDiffusers
+ {
+ ///
+ /// Gets the error diffuser that implements the Atkinson algorithm.
+ ///
+ public static IErrorDiffuser Atkinson { get; } = new AtkinsonDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Burks algorithm.
+ ///
+ public static IErrorDiffuser Burks { get; } = new BurksDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Floyd-Steinberg algorithm.
+ ///
+ public static IErrorDiffuser FloydSteinberg { get; } = new FloydSteinbergDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Jarvis-Judice-Ninke algorithm.
+ ///
+ public static IErrorDiffuser JarvisJudiceNinke { get; } = new JarvisJudiceNinkeDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Sierra-2 algorithm.
+ ///
+ public static IErrorDiffuser Sierra2 { get; } = new Sierra2Diffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Sierra-3 algorithm.
+ ///
+ public static IErrorDiffuser Sierra3 { get; } = new Sierra3Diffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Sierra-Lite algorithm.
+ ///
+ public static IErrorDiffuser SierraLite { get; } = new SierraLiteDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Stevenson-Arce algorithm.
+ ///
+ public static IErrorDiffuser StevensonArce { get; } = new StevensonArceDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Stucki algorithm.
+ ///
+ public static IErrorDiffuser Stucki { get; } = new StuckiDiffuser();
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs
new file mode 100644
index 000000000..0f0338ac7
--- /dev/null
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Dithering.Base;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
+ ///
+ public sealed class StevensonArceDiffuser : ErrorDiffuserBase
+ {
+ ///
+ /// The diffusion matrix
+ ///
+ private static readonly Fast2DArray StevensonArceMatrix =
+ new float[,]
+ {
+ { 0, 0, 0, 0, 0, 32, 0 },
+ { 12, 0, 26, 0, 30, 0, 16 },
+ { 0, 12, 0, 26, 0, 12, 0 },
+ { 5, 0, 12, 0, 12, 0, 5 }
+ };
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public StevensonArceDiffuser()
+ : base(StevensonArceMatrix, 200)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither.cs
deleted file mode 100644
index 685dca5fe..000000000
--- a/src/ImageSharp/Dithering/Ordered/BayerDither.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using SixLabors.ImageSharp.Dithering.Base;
-using SixLabors.ImageSharp.Memory;
-
-namespace SixLabors.ImageSharp.Dithering
-{
- ///
- /// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix.
- ///
- ///
- public sealed class BayerDither : OrderedDitherBase
- {
- ///
- /// The threshold matrix.
- /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1
- ///
- private static readonly Fast2DArray ThresholdMatrix =
- new byte[,]
- {
- { 15, 143, 47, 175 },
- { 207, 79, 239, 111 },
- { 63, 191, 31, 159 },
- { 255, 127, 223, 95 }
- };
-
- ///
- /// Initializes a new instance of the class.
- ///
- public BayerDither()
- : base(ThresholdMatrix)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs b/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
new file mode 100644
index 000000000..1d844c8a7
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies order dithering using the 2x2 Bayer dithering matrix.
+ ///
+ public sealed class BayerDither2x2 : OrderedDither
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BayerDither2x2()
+ : base(2)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs b/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
new file mode 100644
index 000000000..4e9f20beb
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies order dithering using the 4x4 Bayer dithering matrix.
+ ///
+ public sealed class BayerDither4x4 : OrderedDither
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BayerDither4x4()
+ : base(4)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs b/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
new file mode 100644
index 000000000..3ff179a06
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies order dithering using the 8x8 Bayer dithering matrix.
+ ///
+ public sealed class BayerDither8x8 : OrderedDither
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BayerDither8x8()
+ : base(8)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
index 689c9a85b..339f2861d 100644
--- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
+++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs
@@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering
{
///
- /// Encapsulates properties and methods required to perfom ordered dithering on an image.
+ /// Encapsulates properties and methods required to perform ordered dithering on an image.
///
public interface IOrderedDither
{
@@ -17,12 +17,11 @@ namespace SixLabors.ImageSharp.Dithering
/// The source pixel
/// The color to apply to the pixels above the threshold.
/// The color to apply to the pixels below the threshold.
- /// The to pack/unpack to.
- /// The component index to test the threshold against. Must range from 0 to 3.
+ /// The threshold to split the image. Must be between 0 and 1.
/// The column index.
/// The row index.
/// The pixel format.
- void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y)
+ void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y)
where TPixel : struct, IPixel;
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs b/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs
new file mode 100644
index 000000000..e58cbad8a
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Contains reusable static instances of known ordered dither matrices
+ ///
+ public class KnownDitherers
+ {
+ ///
+ /// Gets the order ditherer using the 2x2 Bayer dithering matrix
+ ///
+ public static IOrderedDither BayerDither2x2 { get; } = new BayerDither2x2();
+
+ ///
+ /// Gets the order ditherer using the 3x3 dithering matrix
+ ///
+ public static IOrderedDither OrderedDither3x3 { get; } = new OrderedDither3x3();
+
+ ///
+ /// Gets the order ditherer using the 4x4 Bayer dithering matrix
+ ///
+ public static IOrderedDither BayerDither4x4 { get; } = new BayerDither4x4();
+
+ ///
+ /// Gets the order ditherer using the 8x8 Bayer dithering matrix
+ ///
+ public static IOrderedDither BayerDither8x8 { get; } = new BayerDither8x8();
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs
index 12968914d..c07b185bb 100644
--- a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs
+++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs
@@ -1,36 +1,50 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using SixLabors.ImageSharp.Dithering.Base;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Dithering
{
///
- /// Applies error diffusion based dithering using the 4x4 ordered dithering matrix.
- ///
+ /// An ordered dithering matrix with equal sides of arbitrary length
///
- public sealed class OrderedDither : OrderedDitherBase
+ public class OrderedDither : IOrderedDither
{
- ///
- /// The threshold matrix.
- /// This is calculated by multiplying each value in the original matrix by 16
- ///
- private static readonly Fast2DArray ThresholdMatrix =
- new byte[,]
- {
- { 0, 128, 32, 160 },
- { 192, 64, 224, 96 },
- { 48, 176, 16, 144 },
- { 240, 112, 208, 80 }
- };
+ private readonly Fast2DArray thresholdMatrix;
+ private readonly int modulusX;
+ private readonly int modulusY;
///
/// Initializes a new instance of the class.
///
- public OrderedDither()
- : base(ThresholdMatrix)
+ /// The length of the matrix sides
+ public OrderedDither(uint length)
+ {
+ Fast2DArray ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length);
+ this.modulusX = ditherMatrix.Width;
+ this.modulusY = ditherMatrix.Height;
+
+ // Adjust the matrix range for 0-255
+ // It looks like it's actually possible to dither an image using it's own colors. We should investigate for V2
+ // https://stackoverflow.com/questions/12422407/monochrome-dithering-in-javascript-bayer-atkinson-floyd-steinberg
+ int multiplier = 256 / ditherMatrix.Count;
+ for (int y = 0; y < ditherMatrix.Height; y++)
+ {
+ for (int x = 0; x < ditherMatrix.Width; x++)
+ {
+ ditherMatrix[y, x] = (uint)((ditherMatrix[y, x] + 1) * multiplier) - 1;
+ }
+ }
+
+ this.thresholdMatrix = ditherMatrix;
+ }
+
+ ///
+ public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y)
+ where TPixel : struct, IPixel
{
+ image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs
new file mode 100644
index 000000000..0436b35e9
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies order dithering using the 3x3 dithering matrix.
+ ///
+ public sealed class OrderedDither3x3 : OrderedDither
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OrderedDither3x3()
+ : base(3)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
deleted file mode 100644
index 818a24d5d..000000000
--- a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp.Dithering.Base
-{
- ///
- /// The base class for performing ordered dithering using a 4x4 matrix.
- ///
- public abstract class OrderedDitherBase : IOrderedDither
- {
- ///
- /// The dithering matrix
- ///
- private Fast2DArray matrix;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The thresholding matrix.
- internal OrderedDitherBase(Fast2DArray matrix)
- {
- this.matrix = matrix;
- }
-
- ///
- public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y)
- where TPixel : struct, IPixel
- {
- source.ToRgba32(ref rgba);
- switch (index)
- {
- case 0:
- image[x, y] = this.matrix[y % 3, x % 3] >= rgba.R ? lower : upper;
- return;
- case 1:
- image[x, y] = this.matrix[y % 3, x % 3] >= rgba.G ? lower : upper;
- return;
- case 2:
- image[x, y] = this.matrix[y % 3, x % 3] >= rgba.B ? lower : upper;
- return;
- case 3:
- image[x, y] = this.matrix[y % 3, x % 3] >= rgba.A ? lower : upper;
- return;
- }
-
- throw new ArgumentOutOfRangeException(nameof(index), "Index should be between 0 and 3 inclusive.");
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs
new file mode 100644
index 000000000..fc9ac2551
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// A factory for creating ordered dither matrices.
+ ///
+ internal static class OrderedDitherFactory
+ {
+ ///
+ /// Creates an ordered dithering matrix with equal sides of arbitrary length.
+ ///
+ ///
+ /// The length of the matrix sides
+ /// The
+ public static Fast2DArray CreateDitherMatrix(uint length)
+ {
+ // Calculate the the logarithm of length to the base 2
+ uint exponent = 0;
+ uint bayerLength = 0;
+ do
+ {
+ exponent++;
+ bayerLength = (uint)(1 << (int)exponent);
+ }
+ while (length > bayerLength);
+
+ // Create our Bayer matrix that matches the given exponent and dimensions
+ var matrix = new Fast2DArray((int)length);
+ uint i = 0;
+ for (int y = 0; y < length; y++)
+ {
+ for (int x = 0; x < length; x++)
+ {
+ matrix[y, x] = Bayer(i / length, i % length, exponent);
+ i++;
+ }
+ }
+
+ // If the user requested a matrix with a non-power-of-2 length e.g. 3x3 and we used 4x4 algorithm,
+ // we need to convert the numbers so that the resulting range is un-gapped.
+ // We generated: We saved: We compress the number range:
+ // 0 8 2 10 0 8 2 0 5 2
+ // 12 4 14 6 12 4 14 7 4 8
+ // 3 11 1 9 3 11 1 3 6 1
+ // 15 7 13 5
+ uint maxValue = bayerLength * bayerLength;
+ uint missing = 0;
+ for (uint v = 0; v < maxValue; ++v)
+ {
+ bool found = false;
+ for (int y = 0; y < length; ++y)
+ {
+ for (int x = 0; x < length; x++)
+ {
+ if (matrix[y, x] == v)
+ {
+ matrix[y, x] -= missing;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ ++missing;
+ }
+ }
+
+ return matrix;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint Bayer(uint x, uint y, uint order)
+ {
+ uint result = 0;
+ for (uint i = 0; i < order; ++i)
+ {
+ uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1);
+ uint xOdd = x & 1;
+ result = ((result << 1 | xOdd_XOR_yOdd) << 1) | xOdd;
+ x >>= 1;
+ y >>= 1;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/error_diffusion.txt b/src/ImageSharp/Dithering/error_diffusion.txt
new file mode 100644
index 000000000..ea412f635
--- /dev/null
+++ b/src/ImageSharp/Dithering/error_diffusion.txt
@@ -0,0 +1,58 @@
+List of error diffusion schemes.
+
+Quantization error of *current* pixel is added to the pixels
+on the right and below according to the formulas below.
+This works nicely for most static pictures, but causes
+an avalanche of jittering artifacts if used in animation.
+
+Floyd-Steinberg:
+
+ * 7
+ 3 5 1 / 16
+
+Jarvis-Judice-Ninke:
+
+ * 7 5
+ 3 5 7 5 3
+ 1 3 5 3 1 / 48
+
+Stucki:
+
+ * 8 4
+ 2 4 8 4 2
+ 1 2 4 2 1 / 42
+
+Burkes:
+
+ * 8 4
+ 2 4 8 4 2 / 32
+
+
+Sierra3:
+
+ * 5 3
+ 2 4 5 4 2
+ 2 3 2 / 32
+
+Sierra2:
+
+ * 4 3
+ 1 2 3 2 1 / 16
+
+Sierra-2-4A:
+
+ * 2
+ 1 1 / 4
+
+Stevenson-Arce:
+
+ * . 32
+ 12 . 26 . 30 . 16
+ . 12 . 26 . 12 .
+ 5 . 12 . 12 . 5 / 200
+
+Atkinson:
+
+ * 1 1 / 8
+ 1 1 1
+ 1
diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
index b091467bf..956acc157 100644
--- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs
@@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
public void Configure(Configuration config)
{
- config.SetEncoder(ImageFormats.Bmp, new BmpEncoder());
- config.SetDecoder(ImageFormats.Bmp, new BmpDecoder());
- config.AddImageFormatDetector(new BmpImageFormatDetector());
+ config.ImageFormatsManager.SetEncoder(ImageFormats.Bmp, new BmpEncoder());
+ config.ImageFormatsManager.SetDecoder(ImageFormats.Bmp, new BmpDecoder());
+ config.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector());
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index e552ac104..9f4dba5b4 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -69,7 +69,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private BmpInfoHeader infoHeader;
- private Configuration configuration;
+ private readonly Configuration configuration;
+
+ private readonly MemoryManager memoryManager;
///
/// Initializes a new instance of the class.
@@ -79,6 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
+ this.memoryManager = configuration.MemoryManager;
}
///
@@ -221,9 +224,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
- using (var buffer = Buffer2D.CreateClean(width, height))
+ using (var buffer = this.memoryManager.AllocateClean2D(width, height))
{
- this.UncompressRle8(width, buffer);
+ this.UncompressRle8(width, buffer.Span);
for (int y = 0; y < height; y++)
{
@@ -343,15 +346,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
padding = 4 - padding;
}
- using (var row = Buffer.CreateClean(arrayWidth + padding))
+ using (IManagedByteBuffer row = this.memoryManager.AllocateCleanManagedByteBuffer(arrayWidth + padding))
{
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
+ Span rowSpan = row.Span;
+
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
- this.currentStream.Read(row.Array, 0, row.Length);
+ this.currentStream.Read(row.Array, 0, row.Length());
int offset = 0;
Span pixelRow = pixels.GetRowSpan(newY);
@@ -362,7 +367,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int shift = 0; shift < ppb && (x + shift) < width; shift++)
{
- int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
+ int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int newX = colOffset + shift;
// Stored in b-> g-> r order.
@@ -393,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
- using (var buffer = new Buffer(stride))
+ using (var buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
{
@@ -430,14 +435,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel
{
int padding = CalculatePadding(width, 3);
- using (var row = new PixelArea(width, ComponentOrder.Zyx, padding))
+
+ using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 3, padding))
{
for (int y = 0; y < height; y++)
{
- row.Read(this.currentStream);
-
+ this.currentStream.Read(row);
int newY = Invert(y, height, inverted);
- pixels.CopyFrom(row, newY);
+ Span pixelSpan = pixels.GetRowSpan(newY);
+ PixelOperations.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width);
}
}
}
@@ -454,14 +460,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel
{
int padding = CalculatePadding(width, 4);
- using (var row = new PixelArea(width, ComponentOrder.Zyxw, padding))
+
+ using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 4, padding))
{
for (int y = 0; y < height; y++)
{
- row.Read(this.currentStream);
-
+ this.currentStream.Read(row);
int newY = Invert(y, height, inverted);
- pixels.CopyFrom(row, newY);
+ Span pixelSpan = pixels.GetRowSpan(newY);
+ PixelOperations.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width);
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
index 366afceb5..d80e43c63 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public void Encode(Image image, Stream stream)
where TPixel : struct, IPixel
{
- var encoder = new BmpEncoderCore(this);
+ var encoder = new BmpEncoderCore(this, image.GetMemoryManager());
encoder.Encode(image, stream);
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index d34d17084..66c8b6c08 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Bmp
@@ -21,14 +22,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
/// Gets or sets the number of bits per pixel.
///
- private BmpBitsPerPixel bitsPerPixel;
+ private readonly BmpBitsPerPixel bitsPerPixel;
+
+ private readonly MemoryManager memoryManager;
///
/// Initializes a new instance of the class.
///
/// The encoder options
- public BmpEncoderCore(IBmpEncoderOptions options)
+ /// The memory manager
+ public BmpEncoderCore(IBmpEncoderOptions options, MemoryManager memoryManager)
{
+ this.memoryManager = memoryManager;
this.bitsPerPixel = options.BitsPerPixel;
}
@@ -145,6 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
+ private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel)
+ {
+ return this.memoryManager.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
+ }
+
///
/// Writes the 32bit color palette to the stream.
///
@@ -154,12 +164,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels)
where TPixel : struct, IPixel
{
- using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding))
+ using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
- pixels.CopyTo(row, y);
- writer.Write(row.Bytes, 0, row.Length);
+ Span pixelSpan = pixels.GetRowSpan(y);
+ PixelOperations.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length);
+ writer.Write(row.Array, 0, row.Length());
}
}
}
@@ -173,12 +184,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels)
where TPixel : struct, IPixel
{
- using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding))
+ using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
- pixels.CopyTo(row, y);
- writer.Write(row.Bytes, 0, row.Length);
+ Span pixelSpan = pixels.GetRowSpan(y);
+ PixelOperations.Instance.ToBgr24Bytes(pixelSpan, row.Span, pixelSpan.Length);
+ writer.Write(row.Array, 0, row.Length());
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs
index 935ce8f4a..35e168e27 100644
--- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs
+++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs
@@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is null.
public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder)
where TPixel : struct, IPixel
- => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp));
+ => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Bmp));
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
index 4c42a833c..0bb62779e 100644
--- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
+++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs
@@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
public void Configure(Configuration config)
{
- config.SetEncoder(ImageFormats.Gif, new GifEncoder());
- config.SetDecoder(ImageFormats.Gif, new GifDecoder());
+ config.ImageFormatsManager.SetEncoder(ImageFormats.Gif, new GifEncoder());
+ config.ImageFormatsManager.SetDecoder(ImageFormats.Gif, new GifDecoder());
- config.AddImageFormatDetector(new GifImageFormatDetector());
+ config.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector());
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 3c2251805..48c8ceb8c 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The global color table.
///
- private Buffer globalColorTable;
+ private IManagedByteBuffer globalColorTable;
///
/// The global color table length
@@ -92,6 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
public FrameDecodingMode DecodingMode { get; }
+ private MemoryManager MemoryManager => this.configuration.MemoryManager;
+
///
/// Decodes the stream to the image.
///
@@ -333,18 +335,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
continue;
}
- byte[] commentsBuffer = ArrayPool.Shared.Rent(length);
-
- try
+ using (IManagedByteBuffer commentsBuffer = this.MemoryManager.AllocateManagedByteBuffer(length))
{
- this.currentStream.Read(commentsBuffer, 0, length);
- string comments = this.TextEncoding.GetString(commentsBuffer, 0, length);
+ this.currentStream.Read(commentsBuffer.Array, 0, length);
+ string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length);
this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
- finally
- {
- ArrayPool.Shared.Return(commentsBuffer);
- }
}
}
@@ -359,22 +355,23 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
- Buffer localColorTable = null;
- Buffer indices = null;
+ IManagedByteBuffer localColorTable = null;
+ IManagedByteBuffer indices = null;
try
{
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
if (imageDescriptor.LocalColorTableFlag)
{
int length = imageDescriptor.LocalColorTableSize * 3;
- localColorTable = Buffer.CreateClean(length);
+ localColorTable = this.configuration.MemoryManager.AllocateManagedByteBuffer(length, true);
this.currentStream.Read(localColorTable.Array, 0, length);
}
- indices = Buffer.CreateClean(imageDescriptor.Width * imageDescriptor.Height);
+ indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true);
- this.ReadFrameIndices(imageDescriptor, indices);
- this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor);
+ this.ReadFrameIndices(imageDescriptor, indices.Span);
+ IManagedByteBuffer colorTable = localColorTable ?? this.globalColorTable;
+ this.ReadFrameColors(ref image, ref previousFrame, indices.Span, colorTable.Span, imageDescriptor);
// Skip any remaining blocks
this.Skip(0);
@@ -395,7 +392,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span indices)
{
int dataSize = this.currentStream.ReadByte();
- using (var lzwDecoder = new LzwDecoder(this.currentStream))
+ using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryManager, this.currentStream))
{
lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
}
@@ -445,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
imageFrame = currentFrame;
- this.RestoreToBackground(imageFrame, image.Width, image.Height);
+ this.RestoreToBackground(imageFrame);
}
int i = 0;
@@ -535,9 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The pixel format.
/// The frame.
- /// Width of the image.
- /// Height of the image.
- private void RestoreToBackground(ImageFrame frame, int imageWidth, int imageHeight)
+ private void RestoreToBackground(ImageFrame frame)
where TPixel : struct, IPixel
{
if (this.restoreArea == null)
@@ -545,28 +540,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
return;
}
- // Optimization for when the size of the frame is the same as the image size.
- if (this.restoreArea.Value.Width == imageWidth &&
- this.restoreArea.Value.Height == imageHeight)
- {
- using (PixelAccessor pixelAccessor = frame.Lock())
- {
- pixelAccessor.Reset();
- }
- }
- else
- {
- using (var emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
- {
- using (PixelAccessor pixelAccessor = frame.Lock())
- {
- for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++)
- {
- pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
- }
- }
- }
- }
+ BufferArea pixelArea = frame.PixelBuffer.GetArea(this.restoreArea.Value);
+ pixelArea.Clear();
this.restoreArea = null;
}
@@ -606,7 +581,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
- this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength);
+
+ this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(this.globalColorTableLength, true);
// Read the global color table from the stream
stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs
index ccf46a17d..b548098be 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs
@@ -5,6 +5,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
+
+using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers;
@@ -44,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
public void Encode(Image image, Stream stream)
where TPixel : struct, IPixel
{
- var encoder = new GifEncoderCore(this);
+ var encoder = new GifEncoderCore(image.GetConfiguration().MemoryManager, this);
encoder.Encode(image, stream);
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 41c8e944d..13ca5f2c6 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Text;
using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers;
@@ -18,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
internal sealed class GifEncoderCore
{
+ private readonly MemoryManager memoryManager;
+
///
/// The temp buffer used to reduce allocations.
///
@@ -61,9 +64,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Initializes a new instance of the class.
///
+ /// The to use for buffer allocations.
/// The options for the encoder.
- public GifEncoderCore(IGifEncoderOptions options)
+ public GifEncoderCore(MemoryManager memoryManager, IGifEncoderOptions options)
{
+ this.memoryManager = memoryManager;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
@@ -350,24 +355,21 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Get max colors for bit depth.
int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3;
- byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength);
var rgb = default(Rgb24);
- try
+ using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
{
+ Span colorTableSpan = colorTable.Span;
+
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
image.Palette[i].ToRgb24(ref rgb);
- colorTable[offset] = rgb.R;
- colorTable[offset + 1] = rgb.G;
- colorTable[offset + 2] = rgb.B;
+ colorTableSpan[offset] = rgb.R;
+ colorTableSpan[offset + 1] = rgb.G;
+ colorTableSpan[offset + 2] = rgb.B;
}
- writer.Write(colorTable, 0, colorTableLength);
- }
- finally
- {
- ArrayPool.Shared.Return(colorTable);
+ writer.Write(colorTable.Array, 0, colorTableLength);
}
}
@@ -380,7 +382,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer)
where TPixel : struct, IPixel
{
- using (var encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth))
+ using (var encoder = new LzwEncoder(this.memoryManager, image.Pixels, (byte)this.bitDepth))
{
encoder.Encode(writer.BaseStream);
}
diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs
index 939eb456e..78acadb4b 100644
--- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs
+++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs
@@ -36,6 +36,6 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is null.
public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder)
where TPixel : struct, IPixel
- => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif));
+ => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Gif));
}
}
diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
index 3284dad65..0c73efea4 100644
--- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
@@ -5,6 +5,8 @@ using System;
using System.Buffers;
using System.IO;
+using SixLabors.ImageSharp.Memory;
+
namespace SixLabors.ImageSharp.Formats.Gif
{
///
@@ -30,17 +32,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The prefix buffer.
///
- private readonly int[] prefix;
+ private readonly IBuffer prefix;
///
/// The suffix buffer.
///
- private readonly int[] suffix;
+ private readonly IBuffer suffix;
///
/// The pixel stack buffer.
///
- private readonly int[] pixelStack;
+ private readonly IBuffer pixelStack;
///
/// A value indicating whether this instance of the given entity has been disposed.
@@ -59,21 +61,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Initializes a new instance of the class
/// and sets the stream, where the compressed data should be read from.
///
+ /// The to use for buffer allocations.
/// The stream to read from.
/// is null.
- public LzwDecoder(Stream stream)
+ public LzwDecoder(MemoryManager memoryManager, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
this.stream = stream;
- this.prefix = ArrayPool.Shared.Rent(MaxStackSize);
- this.suffix = ArrayPool.Shared.Rent(MaxStackSize);
- this.pixelStack = ArrayPool.Shared.Rent(MaxStackSize + 1);
-
- Array.Clear(this.prefix, 0, MaxStackSize);
- Array.Clear(this.suffix, 0, MaxStackSize);
- Array.Clear(this.pixelStack, 0, MaxStackSize + 1);
+ this.prefix = memoryManager.Allocate(MaxStackSize, true);
+ this.suffix = memoryManager.Allocate(MaxStackSize, true);
+ this.pixelStack = memoryManager.Allocate(MaxStackSize + 1, true);
}
///
@@ -116,10 +115,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
int data = 0;
int first = 0;
+ Span prefixSpan = this.prefix.Span;
+ Span suffixSpan = this.suffix.Span;
+ Span pixelStackSpan = this.pixelStack.Span;
+
for (code = 0; code < clearCode; code++)
{
- this.prefix[code] = 0;
- this.suffix[code] = (byte)code;
+ prefixSpan[code] = 0;
+ suffixSpan[code] = (byte)code;
}
byte[] buffer = new byte[255];
@@ -173,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (oldCode == NullCode)
{
- this.pixelStack[top++] = this.suffix[code];
+ pixelStackSpan[top++] = suffixSpan[code];
oldCode = code;
first = code;
continue;
@@ -182,27 +185,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
int inCode = code;
if (code == availableCode)
{
- this.pixelStack[top++] = (byte)first;
+ pixelStackSpan[top++] = (byte)first;
code = oldCode;
}
while (code > clearCode)
{
- this.pixelStack[top++] = this.suffix[code];
- code = this.prefix[code];
+ pixelStackSpan[top++] = suffixSpan[code];
+ code = prefixSpan[code];
}
- first = this.suffix[code];
+ first = suffixSpan[code];
- this.pixelStack[top++] = this.suffix[code];
+ pixelStackSpan[top++] = suffixSpan[code];
// Fix for Gifs that have "deferred clear code" as per here :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if (availableCode < MaxStackSize)
{
- this.prefix[availableCode] = oldCode;
- this.suffix[availableCode] = first;
+ prefixSpan[availableCode] = oldCode;
+ suffixSpan[availableCode] = first;
availableCode++;
if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
{
@@ -218,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--;
// Clear missing pixels
- pixels[xyz++] = (byte)this.pixelStack[top];
+ pixels[xyz++] = (byte)pixelStackSpan[top];
}
}
@@ -262,9 +265,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (disposing)
{
- ArrayPool.Shared.Return(this.prefix);
- ArrayPool.Shared.Return(this.suffix);
- ArrayPool.Shared.Return(this.pixelStack);
+ this.prefix?.Dispose();
+ this.suffix?.Dispose();
+ this.pixelStack?.Dispose();
}
this.isDisposed = true;
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index b7bfd0fd2..35c414896 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -5,6 +5,8 @@ using System;
using System.Buffers;
using System.IO;
+using SixLabors.ImageSharp.Memory;
+
namespace SixLabors.ImageSharp.Formats.Gif
{
///
@@ -69,12 +71,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The hash table.
///
- private readonly int[] hashTable;
+ private readonly IBuffer hashTable;
///
/// The code table.
///
- private readonly int[] codeTable;
+ private readonly IBuffer codeTable;
///
/// Define the storage for the packet accumulator.
@@ -189,17 +191,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Initializes a new instance of the class.
///
+ /// The to use for buffer allocations.
/// The array of indexed pixels.
/// The color depth in bits.
- public LzwEncoder(byte[] indexedPixels, int colorDepth)
+ public LzwEncoder(MemoryManager memoryManager, byte[] indexedPixels, int colorDepth)
{
this.pixelArray = indexedPixels;
this.initialCodeSize = Math.Max(2, colorDepth);
- this.hashTable = ArrayPool.Shared.Rent(HashSize);
- this.codeTable = ArrayPool.Shared.Rent(HashSize);
- Array.Clear(this.hashTable, 0, HashSize);
- Array.Clear(this.codeTable, 0, HashSize);
+ this.hashTable = memoryManager.Allocate(HashSize, true);
+ this.codeTable = memoryManager.Allocate(HashSize, true);
}
///
@@ -258,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The output stream.
private void ClearBlock(Stream stream)
{
- this.ResetCodeTable(this.hsize);
+ this.ResetCodeTable();
this.freeEntry = this.clearCode + 2;
this.clearFlag = true;
@@ -268,13 +269,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Reset the code table.
///
- /// The hash size.
- private void ResetCodeTable(int size)
+ private void ResetCodeTable()
{
- for (int i = 0; i < size; ++i)
- {
- this.hashTable[i] = -1;
- }
+ this.hashTable.Span.Fill(-1);
+
+ // Original code:
+ // for (int i = 0; i < size; ++i)
+ // {
+ // this.hashTable[i] = -1;
+ // }
}
///
@@ -316,23 +319,26 @@ namespace SixLabors.ImageSharp.Formats.Gif
hsizeReg = this.hsize;
- this.ResetCodeTable(hsizeReg); // clear hash table
+ this.ResetCodeTable(); // clear hash table
this.Output(this.clearCode, stream);
+ Span hashTableSpan = this.hashTable.Span;
+ Span codeTableSpan = this.codeTable.Span;
+
while ((c = this.NextPixel()) != Eof)
{
fcode = (c << this.maxbits) + ent;
int i = (c << hshift) ^ ent /* = 0 */;
- if (this.hashTable[i] == fcode)
+ if (hashTableSpan[i] == fcode)
{
- ent = this.codeTable[i];
+ ent = codeTableSpan[i];
continue;
}
// Non-empty slot
- if (this.hashTable[i] >= 0)
+ if (hashTableSpan[i] >= 0)
{
int disp = hsizeReg - i;
if (i == 0)
@@ -347,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
i += hsizeReg;
}
- if (this.hashTable[i] == fcode)
+ if (hashTableSpan[i] == fcode)
{
- ent = this.codeTable[i];
+ ent = codeTableSpan[i];
break;
}
}
- while (this.hashTable[i] >= 0);
+ while (hashTableSpan[i] >= 0);
- if (this.hashTable[i] == fcode)
+ if (hashTableSpan[i] == fcode)
{
continue;
}
@@ -365,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = c;
if (this.freeEntry < this.maxmaxcode)
{
- this.codeTable[i] = this.freeEntry++; // code -> hashtable
- this.hashTable[i] = fcode;
+ codeTableSpan[i] = this.freeEntry++; // code -> hashtable
+ hashTableSpan[i] = fcode;
}
else
{
@@ -483,8 +489,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (disposing)
{
- ArrayPool.Shared.Return(this.hashTable);
- ArrayPool.Shared.Return(this.codeTable);
+ this.hashTable?.Dispose();
+ this.codeTable?.Dispose();
}
this.isDisposed = true;
diff --git a/src/ImageSharp/Formats/Gif/PackedField.cs b/src/ImageSharp/Formats/Gif/PackedField.cs
index 962e2082b..28a415e2b 100644
--- a/src/ImageSharp/Formats/Gif/PackedField.cs
+++ b/src/ImageSharp/Formats/Gif/PackedField.cs
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Represents a byte of data in a GIF data stream which contains a number
/// of data items.
///
- internal struct PackedField : IEquatable
+ internal readonly struct PackedField : IEquatable
{
///
/// The individual bits representing the packed byte.
diff --git a/src/ImageSharp/Formats/ImageFormatManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs
new file mode 100644
index 000000000..67ba11147
--- /dev/null
+++ b/src/ImageSharp/Formats/ImageFormatManager.cs
@@ -0,0 +1,186 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SixLabors.ImageSharp.Formats
+{
+ ///
+ /// Collection of Image Formats to be used in class.
+ ///
+ public class ImageFormatManager
+ {
+ ///
+ /// The list of supported keyed to mime types.
+ ///
+ private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary();
+
+ ///
+ /// The list of supported keyed to mime types.
+ ///
+ private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary();
+
+ ///
+ /// The list of supported s.
+ ///
+ private readonly ConcurrentBag imageFormats = new ConcurrentBag();
+
+ ///
+ /// The list of supported s.
+ ///
+ private ConcurrentBag imageFormatDetectors = new ConcurrentBag();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ImageFormatManager()
+ {
+ }
+
+ ///
+ /// Gets the maximum header size of all the formats.
+ ///
+ internal int MaxHeaderSize { get; private set; }
+
+ ///
+ /// Gets the currently registered s.
+ ///
+ public IEnumerable ImageFormats => this.imageFormats;
+
+ ///
+ /// Gets the currently registered s.
+ ///
+ internal IEnumerable FormatDetectors => this.imageFormatDetectors;
+
+ ///
+ /// Gets the currently registered s.
+ ///
+ internal IEnumerable> ImageDecoders => this.mimeTypeDecoders;
+
+ ///
+ /// Gets the currently registered s.
+ ///
+ internal IEnumerable> ImageEncoders => this.mimeTypeEncoders;
+
+ ///
+ /// Registers a new format provider.
+ ///
+ /// The format to register as a known format.
+ public void AddImageFormat(IImageFormat format)
+ {
+ Guard.NotNull(format, nameof(format));
+ Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
+ Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
+ this.imageFormats.Add(format);
+ }
+
+ ///
+ /// For the specified file extensions type find the e .
+ ///
+ /// The extension to discover
+ /// The if found otherwise null
+ public IImageFormat FindFormatByFileExtension(string extension)
+ {
+ return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
+ }
+
+ ///
+ /// For the specified mime type find the .
+ ///
+ /// The mime-type to discover
+ /// The if found; otherwise null
+ public IImageFormat FindFormatByMimeType(string mimeType)
+ {
+ return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
+ }
+
+ ///
+ /// Sets a specific image encoder as the encoder for a specific image format.
+ ///
+ /// The image format to register the encoder for.
+ /// The encoder to use,
+ public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
+ {
+ Guard.NotNull(imageFormat, nameof(imageFormat));
+ Guard.NotNull(encoder, nameof(encoder));
+ this.AddImageFormat(imageFormat);
+ this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
+ }
+
+ ///
+ /// Sets a specific image decoder as the decoder for a specific image format.
+ ///
+ /// The image format to register the encoder for.
+ /// The decoder to use,
+ 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);
+ }
+
+ ///
+ /// Removes all the registered image format detectors.
+ ///
+ public void ClearImageFormatDetectors()
+ {
+ this.imageFormatDetectors = new ConcurrentBag();
+ }
+
+ ///
+ /// Adds a new detector for detecting mime types.
+ ///
+ /// The detector to add
+ public void AddImageFormatDetector(IImageFormatDetector detector)
+ {
+ Guard.NotNull(detector, nameof(detector));
+ this.imageFormatDetectors.Add(detector);
+ this.SetMaxHeaderSize();
+ }
+
+ ///
+ /// For the specified mime type find the decoder.
+ ///
+ /// The format to discover
+ /// The if found otherwise null
+ public IImageDecoder FindDecoder(IImageFormat format)
+ {
+ Guard.NotNull(format, nameof(format));
+ if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder))
+ {
+ return decoder;
+ }
+
+ return null;
+ }
+
+ ///
+ /// For the specified mime type find the encoder.
+ ///
+ /// The format to discover
+ /// The if found otherwise null
+ public IImageEncoder FindEncoder(IImageFormat format)
+ {
+ Guard.NotNull(format, nameof(format));
+ if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder))
+ {
+ return encoder;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Sets the max header size.
+ ///
+ private void SetMaxHeaderSize()
+ {
+ this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
index 1066cfa80..11a456ef9 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
@@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public Block8x8(Span coefficients)
{
ref byte selfRef = ref Unsafe.As(ref this);
- ref byte sourceRef = ref coefficients.NonPortableCast().DangerousGetPinnableReference();
+ ref byte sourceRef = ref MemoryMarshal.GetReference(coefficients.NonPortableCast());
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
@@ -204,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void CopyTo(Span destination)
{
ref byte selfRef = ref Unsafe.As(ref this);
- ref byte destRef = ref destination.NonPortableCast().DangerousGetPinnableReference();
+ ref byte destRef = ref MemoryMarshal.GetReference(destination.NonPortableCast());
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs
index 39a6bee2e..d8963a8b6 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs
@@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return;
}
+ ref float destBase = ref area.GetReferenceToOrigin();
+
// TODO: Optimize: implement all the cases with loopless special code! (T4?)
for (int y = 0; y < 8; y++)
{
@@ -40,9 +42,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
for (int i = 0; i < verticalScale; i++)
{
+ int baseIdx = ((yy + i) * area.Stride) + xx;
+
for (int j = 0; j < horizontalScale; j++)
{
- area[xx + j, yy + i] = value;
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref destBase, baseIdx + j) = value;
}
}
}
@@ -53,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void CopyTo(BufferArea area)
{
ref byte selfBase = ref Unsafe.As(ref this);
- ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigo());
+ ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigin());
int destStride = area.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
@@ -76,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
private void CopyTo2x2(BufferArea area)
{
- ref float destBase = ref area.GetReferenceToOrigo();
+ ref float destBase = ref area.GetReferenceToOrigin();
int destStride = area.Stride;
this.WidenCopyImpl2x2(ref destBase, 0, destStride);
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
index 2dd42288c..f45b5df4e 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
@@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void LoadFrom(Span source)
{
- ref byte s = ref Unsafe.As(ref source.DangerousGetPinnableReference());
+ ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
ref byte d = ref Unsafe.As(ref this);
Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
@@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(Span dest)
{
- ref byte d = ref Unsafe.As(ref dest.DangerousGetPinnableReference());
+ ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
ref byte s = ref Unsafe.As(ref this);
Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float));
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
index 0ec2297d7..d55e36bd4 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// Provides information about the Adobe marker segment.
///
/// See the included 5116.DCT.pdf file in the source for more information.
- internal struct AdobeMarker : IEquatable
+ internal readonly struct AdobeMarker : IEquatable
{
///
/// Gets the length of an adobe marker segment.
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
index a7fc136af..2f214f88a 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
@@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
@@ -37,14 +38,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!");
ref Vector4Pair yBase =
- ref Unsafe.As(ref values.Component0.DangerousGetPinnableReference());
+ ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component0));
ref Vector4Pair cbBase =
- ref Unsafe.As(ref values.Component1.DangerousGetPinnableReference());
+ ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component1));
ref Vector4Pair crBase =
- ref Unsafe.As(ref values.Component2.DangerousGetPinnableReference());
+ ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
- ref Unsafe.As(ref result.DangerousGetPinnableReference());
+ ref Unsafe.As(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector4(-128f);
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
index 77e74c32b..f8a451422 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
@@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Tuples;
// ReSharper disable ImpureMethodCallOnReadonlyValueField
@@ -46,14 +47,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
}
ref Vector yBase =
- ref Unsafe.As>(ref values.Component0.DangerousGetPinnableReference());
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
ref Vector cbBase =
- ref Unsafe.As>(ref values.Component1.DangerousGetPinnableReference());
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1));
ref Vector crBase =
- ref Unsafe.As>(ref values.Component2.DangerousGetPinnableReference());
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2));
ref Vector4Octet resultBase =
- ref Unsafe.As(ref result.DangerousGetPinnableReference());
+ ref Unsafe.As(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector(-128f);
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs
index e0abc3215..187b65f72 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs
@@ -66,7 +66,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters
///
/// A stack-only struct to reference the input buffers using -s.
///
- public struct ComponentValues
+#pragma warning disable SA1206 // Declaration keywords should follow order
+ public readonly ref struct ComponentValues
+#pragma warning restore SA1206 // Declaration keywords should follow order
{
///
/// The component count
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
index cba7be553..c856fd04a 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// Provides information about the JFIF marker segment
/// TODO: Thumbnail?
///
- internal struct JFifMarker : IEquatable
+ internal readonly struct JFifMarker : IEquatable
{
///
/// Gets the length of an JFIF marker segment.
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs
index 574967b6b..5e8e8fa2c 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs
@@ -8,7 +8,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
{
///
- /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels.
+ /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels.
///
[StructLayout(LayoutKind.Sequential)]
internal struct JpegBlockPostProcessor
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs
index 87c1431e0..1be637b6d 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs
@@ -22,11 +22,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
///
/// Initializes a new instance of the class.
///
- public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
+ public JpegComponentPostProcessor(MemoryManager memoryManager, JpegImagePostProcessor imagePostProcessor, IJpegComponent component)
{
this.Component = component;
this.ImagePostProcessor = imagePostProcessor;
- this.ColorBuffer = new Buffer2D(imagePostProcessor.PostProcessorBufferSize);
+ this.ColorBuffer = memoryManager.Allocate2D(
+ imagePostProcessor.PostProcessorBufferSize.Width,
+ imagePostProcessor.PostProcessorBufferSize.Height);
this.BlockRowsPerStep = JpegImagePostProcessor.BlockRowsPerStep / this.Component.SubSamplingDivisors.Height;
this.blockAreaSize = this.Component.SubSamplingDivisors * 8;
@@ -43,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
public IJpegComponent Component { get; }
///
- /// Gets the temporal working buffer of color values.
+ /// Gets the temporary working buffer of color values.
///
public Buffer2D ColorBuffer { get; }
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
index 125ec5272..2adf3e02d 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs
@@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
///
/// Temporal buffer to store a row of colors.
///
- private readonly Buffer rgbaBuffer;
+ private readonly IBuffer rgbaBuffer;
///
/// The corresponding to the current determined by .
@@ -44,16 +44,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
///
/// Initializes a new instance of the class.
///
+ /// The to use for buffer allocations.
/// The representing the uncompressed spectral Jpeg data
- public JpegImagePostProcessor(IRawJpegData rawJpeg)
+ public JpegImagePostProcessor(MemoryManager memoryManager, IRawJpegData rawJpeg)
{
this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First();
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
- this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray();
- this.rgbaBuffer = new Buffer(rawJpeg.ImageSizeInPixels.Width);
+ this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryManager, this, c)).ToArray();
+ this.rgbaBuffer = memoryManager.Allocate(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
}
@@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
public int NumberOfPostProcessorSteps { get; }
///
- /// Gets the size of the temporal buffers we need to allocate into .
+ /// Gets the size of the temporary buffers we need to allocate into .
///
public Size PostProcessorBufferSize { get; }
@@ -149,11 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
int y = yy - this.PixelRowCounter;
var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y);
- this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer);
+ this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer.Span);
Span destRow = destination.GetPixelRowSpan(yy);
- PixelOperations.Instance.PackFromVector4(this.rgbaBuffer, destRow, destination.Width);
+ PixelOperations