diff --git a/ImageSharp.ruleset b/ImageSharp.ruleset
new file mode 100644
index 000000000..554dc16dd
--- /dev/null
+++ b/ImageSharp.ruleset
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/ImageSharp.sln b/ImageSharp.sln
index f05c47e40..cd9e39a33 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -20,8 +20,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
build\dotnet-latest.ps1 = build\dotnet-latest.ps1
features.md = features.md
global.json = global.json
+ ImageSharp.ruleset = ImageSharp.ruleset
+ NuGet.config = NuGet.config
build\package.json = build\package.json
README.md = README.md
+ Rebracer.xml = Rebracer.xml
Settings.StyleCop = Settings.StyleCop
EndProjectSection
EndProject
@@ -29,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C06
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{56801022-D71A-4FBE-BC5B-CBA08E2284EC}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Tests46", "tests\ImageSharp.Tests46\ImageSharp.Tests46.csproj", "{88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -47,6 +52,10 @@ Global
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{299D8E18-102C-42DE-ADBF-79098EE706A8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -55,5 +64,6 @@ Global
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{299D8E18-102C-42DE-ADBF-79098EE706A8} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
+ {88C5FB74-5845-4CC0-8F1E-A25EBABC95C2} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
EndGlobalSection
EndGlobal
diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings
index 5acec071a..b0f5aa692 100644
--- a/ImageSharp.sln.DotSettings
+++ b/ImageSharp.sln.DotSettings
@@ -345,6 +345,7 @@
FDCT
IDCT
JPEG
+ MCU
PNG
RGB
RLE
diff --git a/README.md b/README.md
index bd122839d..24145f7f9 100644
--- a/README.md
+++ b/README.md
@@ -98,3 +98,6 @@ Grand High Eternal Dictator
Core Team
- [Dirk Lemstra](https://github.com/dlemstra)
- [Jeavon Leopold](https://github.com/jeavon)
+- [Anton Firsov](https://github.com/antonfirsov)
+- [Olivia Ifrim](https://github.com/olivif)
+- [Scott Williams](https://github.com/tocsoft)
diff --git a/global.json b/global.json
index 7346bdc28..6da79b611 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"projects": [ "src" ],
"sdk": {
- "version": "1.0.0-preview2-003121"
+ "version": "1.0.0-preview2-003131"
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs
index 109544ada..036ee14c1 100644
--- a/src/ImageSharp/Colors/Color.cs
+++ b/src/ImageSharp/Colors/Color.cs
@@ -273,35 +273,37 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = this.B;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.R;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = this.B;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.R;
- bytes[startIndex + 3] = this.A;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = this.R;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.B;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = this.R;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.B;
- bytes[startIndex + 3] = this.A;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = this.R;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.B;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = this.R;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.B;
+ bytes[startIndex + 3] = this.A;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = this.B;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.R;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = this.B;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.R;
+ bytes[startIndex + 3] = this.A;
}
///
diff --git a/src/ImageSharp/Colors/ColorspaceTransforms.cs b/src/ImageSharp/Colors/ColorspaceTransforms.cs
index 0c70dd98b..cda702270 100644
--- a/src/ImageSharp/Colors/ColorspaceTransforms.cs
+++ b/src/ImageSharp/Colors/ColorspaceTransforms.cs
@@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.Numerics;
+ using Colors.Spaces;
///
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
@@ -18,11 +19,6 @@ namespace ImageSharp
///
public partial struct Color
{
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001F;
-
///
/// Allows the implicit conversion of an instance of to a
/// .
@@ -62,9 +58,9 @@ namespace ImageSharp
///
public static implicit operator Color(YCbCr color)
{
- byte y = color.Y;
- int cb = color.Cb - 128;
- int cr = color.Cr - 128;
+ float y = color.Y;
+ float cb = color.Cb - 128;
+ float cr = color.Cr - 128;
byte r = (byte)(y + (1.402F * cr)).Clamp(0, 255);
byte g = (byte)(y - (0.34414F * cb) - (0.71414F * cr)).Clamp(0, 255);
@@ -109,12 +105,12 @@ namespace ImageSharp
float s = color.S;
float v = color.V;
- if (Math.Abs(s) < Epsilon)
+ if (Math.Abs(s) < Constants.Epsilon)
{
return new Color(v, v, v, 1);
}
- float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
+ float h = (Math.Abs(color.H - 360) < Constants.Epsilon) ? 0 : color.H / 60;
int i = (int)Math.Truncate(h);
float f = h - i;
@@ -182,9 +178,9 @@ namespace ImageSharp
float s = color.S;
float l = color.L;
- if (Math.Abs(l) > Epsilon)
+ if (Math.Abs(l) > Constants.Epsilon)
{
- if (Math.Abs(s) < Epsilon)
+ if (Math.Abs(s) < Constants.Epsilon)
{
r = g = b = l;
}
diff --git a/src/ImageSharp/Colors/ComponentOrder.cs b/src/ImageSharp/Colors/ComponentOrder.cs
index 7372ab9bf..03fa3bbf8 100644
--- a/src/ImageSharp/Colors/ComponentOrder.cs
+++ b/src/ImageSharp/Colors/ComponentOrder.cs
@@ -13,21 +13,21 @@ namespace ImageSharp
///
/// Z-> Y-> X order. Equivalent to B-> G-> R in
///
- ZYX,
+ Zyx,
///
/// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in
///
- ZYXW,
+ Zyxw,
///
/// X-> Y-> Z order. Equivalent to R-> G-> B in
///
- XYZ,
+ Xyz,
///
/// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in
///
- XYZW,
+ Xyzw,
}
}
diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs
index 0100afee1..361fe5b9e 100644
--- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs
@@ -74,35 +74,37 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = 0;
- bytes[startIndex + 2] = 0;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = 0;
- bytes[startIndex + 2] = 0;
- bytes[startIndex + 3] = this.PackedValue;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = 0;
- bytes[startIndex + 2] = 0;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = 0;
- bytes[startIndex + 2] = 0;
- bytes[startIndex + 3] = this.PackedValue;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = 0;
+ bytes[startIndex + 2] = 0;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = 0;
+ bytes[startIndex + 2] = 0;
+ bytes[startIndex + 3] = this.PackedValue;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = 0;
+ bytes[startIndex + 2] = 0;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = 0;
+ bytes[startIndex + 2] = 0;
+ bytes[startIndex + 3] = this.PackedValue;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs
index 996b8f101..8ab8e3f43 100644
--- a/src/ImageSharp/Colors/PackedPixel/Argb.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs
@@ -220,36 +220,38 @@ namespace ImageSharp
this.PackedValue = Pack(x, y, z, w);
}
- ///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ ///
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = this.B;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.R;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = this.B;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.R;
- bytes[startIndex + 3] = this.A;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = this.R;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.B;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = this.R;
- bytes[startIndex + 1] = this.G;
- bytes[startIndex + 2] = this.B;
- bytes[startIndex + 3] = this.A;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = this.R;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.B;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = this.R;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.B;
+ bytes[startIndex + 3] = this.A;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = this.B;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.R;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ bytes[startIndex] = this.B;
+ bytes[startIndex + 1] = this.G;
+ bytes[startIndex + 2] = this.R;
+ bytes[startIndex + 3] = this.A;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs
index e28c7aed0..1f1ce0a17 100644
--- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs
@@ -96,37 +96,41 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- default:
- throw new NotSupportedException();
- }
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs
index ed07a69c0..1f33cb791 100644
--- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs
@@ -88,37 +88,41 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs
index 97153af9d..d0c52068d 100644
--- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs
@@ -88,37 +88,41 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs
index 31efd6094..69c69ecaf 100644
--- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs
@@ -95,37 +95,41 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs
index b2919b46a..22e32aa56 100644
--- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs
+++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs
@@ -97,40 +97,57 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs
index c41e01b2a..7c17dcea8 100644
--- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs
+++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs
@@ -111,40 +111,57 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs
index 635155f0c..a7f10fc71 100644
--- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs
+++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs
@@ -105,40 +105,57 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs b/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs
index 5793ea1ba..3343a92c7 100644
--- a/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs
+++ b/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs
@@ -12,7 +12,7 @@ namespace ImageSharp
public interface IPackedBytes
{
///
- /// Gets the packed representation from the gives bytes.
+ /// Sets the packed representation from the given byte array.
///
/// The x-component.
/// The y-component.
@@ -21,11 +21,35 @@ namespace ImageSharp
void PackFromBytes(byte x, byte y, byte z, byte w);
///
- /// Sets the packed representation into the gives bytes.
+ /// Expands the packed representation into a given byte array.
+ /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in
///
/// The bytes to set the color in.
/// The starting index of the .
- /// The order of the components.
- void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder);
+ void ToXyzBytes(byte[] bytes, int startIndex);
+
+ ///
+ /// Expands the packed representation into a given byte array.
+ /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in
+ ///
+ /// The bytes to set the color in.
+ /// The starting index of the .
+ void ToXyzwBytes(byte[] bytes, int startIndex);
+
+ ///
+ /// Expands the packed representation into a given byte array.
+ /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in
+ ///
+ /// The bytes to set the color in.
+ /// The starting index of the .
+ void ToZyxBytes(byte[] bytes, int startIndex);
+
+ ///
+ /// Expands the packed representation into a given byte array.
+ /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in
+ ///
+ /// The bytes to set the color in.
+ /// The starting index of the .
+ void ToZyxwBytes(byte[] bytes, int startIndex);
}
}
diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs
index 3ff419ab6..f1dae1066 100644
--- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs
+++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs
@@ -120,7 +120,7 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= Half;
@@ -129,33 +129,56 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = 255;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = 0;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = 0;
- bytes[startIndex + 3] = 255;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = 0;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = 0;
+ bytes[startIndex + 3] = 255;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = 255;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs
index 2638ff7f5..22eba5182 100644
--- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs
+++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs
@@ -114,7 +114,7 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= Half;
@@ -123,33 +123,56 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
index 0d63f1b4d..34b2fc320 100644
--- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
+++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
@@ -108,7 +108,7 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= Half;
@@ -117,33 +117,56 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = 255;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = 0;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = 0;
- bytes[startIndex + 3] = 255;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = 0;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = 0;
+ bytes[startIndex + 3] = 255;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = 255;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
index 0f43c54b1..9742a5f34 100644
--- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
+++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
@@ -116,7 +116,7 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector *= Half;
@@ -125,33 +125,56 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector *= Half;
+ vector += Round;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs
index 8ca53a829..d885a4470 100644
--- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs
@@ -100,37 +100,45 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)vector.Z;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.X;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)vector.X;
- bytes[startIndex + 1] = (byte)vector.Y;
- bytes[startIndex + 2] = (byte)vector.Z;
- bytes[startIndex + 3] = (byte)vector.W;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 3] = (byte)vector.W;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 3] = (byte)vector.W;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
index 54ac6279f..da7dbe1ee 100644
--- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
@@ -95,37 +95,45 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs
index 2b9932519..64631f1e1 100644
--- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs
@@ -94,37 +94,45 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4() * 255F;
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4() * 255F;
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs
index 310c44ec8..d45a80fcb 100644
--- a/src/ImageSharp/Colors/PackedPixel/Short2.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs
@@ -106,7 +106,7 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
@@ -115,33 +115,56 @@ namespace ImageSharp
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = 0;
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = 255;
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = 0;
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = 0;
- bytes[startIndex + 3] = 255;
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = 0;
+ }
+
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector2 vector = this.ToVector2();
+ vector /= 65534;
+ vector *= 255;
+ vector += Half;
+ vector += Round;
+ vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = 0;
+ bytes[startIndex + 3] = 255;
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector2 vector = this.ToVector2();
+ vector /= 65534;
+ vector *= 255;
+ vector += Half;
+ vector += Round;
+ vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
+
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector2 vector = this.ToVector2();
+ vector /= 65534;
+ vector *= 255;
+ vector += Half;
+ vector += Round;
+ vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
+
+ bytes[startIndex] = 0;
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = 255;
}
///
diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs
index 13bce08c8..cd112a90f 100644
--- a/src/ImageSharp/Colors/PackedPixel/Short4.cs
+++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs
@@ -112,7 +112,7 @@ namespace ImageSharp
}
///
- public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
+ public void ToXyzBytes(byte[] bytes, int startIndex)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
@@ -121,50 +121,65 @@ namespace ImageSharp
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
- switch (componentOrder)
- {
- case ComponentOrder.ZYX:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- break;
- case ComponentOrder.ZYXW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- case ComponentOrder.XYZ:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- break;
- case ComponentOrder.XYZW:
- bytes[startIndex] = (byte)(float)Math.Round(vector.X);
- bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
- bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
- bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
- break;
- default:
- throw new NotSupportedException();
- }
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
}
- ///
- /// Returns a value that indicates whether the current instance is equal to a specified object.
- ///
- /// The object with which to make the comparison.
- /// true if the current instance is equal to the specified object; false otherwise.
+ ///
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector /= 65534;
+ vector *= 255;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
+ }
+
+ ///
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector /= 65534;
+ vector *= 255;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ }
+
+ ///
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = this.ToVector4();
+ vector /= 65534;
+ vector *= 255;
+ vector += Half;
+ vector += Round;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+
+ bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
+ bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
+ bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
+ bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
+ }
+
+ ///
public override bool Equals(object obj)
{
return (obj is Short4) && this == (Short4)obj;
}
- ///
- /// Returns a value that indicates whether the current instance is equal to a specified object.
- ///
- /// The object with which to make the comparison.
- /// true if the current instance is equal to the specified object; false otherwise.
+ ///
public bool Equals(Short4 other)
{
return this == other;
diff --git a/src/ImageSharp/Colors/Colorspaces/Bgra32.cs b/src/ImageSharp/Colors/Spaces/Bgra32.cs
similarity index 90%
rename from src/ImageSharp/Colors/Colorspaces/Bgra32.cs
rename to src/ImageSharp/Colors/Spaces/Bgra32.cs
index ff9203b03..09475f42c 100644
--- a/src/ImageSharp/Colors/Colorspaces/Bgra32.cs
+++ b/src/ImageSharp/Colors/Spaces/Bgra32.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@@ -22,7 +22,7 @@ namespace ImageSharp
///
/// The backing vector for SIMD support.
///
- private Vector4 backingVector;
+ private readonly Vector4 backingVector;
///
/// Initializes a new instance of the struct.
@@ -133,7 +133,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- return GetHashCode(this);
+ return this.backingVector.GetHashCode();
}
///
@@ -152,16 +152,5 @@ namespace ImageSharp
{
return this.backingVector.Equals(other.backingVector);
}
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageSharp/Colors/Colorspaces/CieLab.cs b/src/ImageSharp/Colors/Spaces/CieLab.cs
similarity index 88%
rename from src/ImageSharp/Colors/Colorspaces/CieLab.cs
rename to src/ImageSharp/Colors/Spaces/CieLab.cs
index e3300f3b8..f4f349b52 100644
--- a/src/ImageSharp/Colors/Colorspaces/CieLab.cs
+++ b/src/ImageSharp/Colors/Spaces/CieLab.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@@ -20,15 +20,10 @@ namespace ImageSharp
///
public static readonly CieLab Empty = default(CieLab);
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001f;
-
///
/// The backing vector for SIMD support.
///
- private Vector3 backingVector;
+ private readonly Vector3 backingVector;
///
/// Initializes a new instance of the struct.
@@ -138,7 +133,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- return GetHashCode(this);
+ return this.backingVector.GetHashCode();
}
///
@@ -166,7 +161,7 @@ namespace ImageSharp
///
public bool Equals(CieLab other)
{
- return this.AlmostEquals(other, Epsilon);
+ return this.AlmostEquals(other, Constants.Epsilon);
}
///
@@ -178,16 +173,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(CieLab color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageSharp/Colors/Colorspaces/CieXyz.cs b/src/ImageSharp/Colors/Spaces/CieXyz.cs
similarity index 87%
rename from src/ImageSharp/Colors/Colorspaces/CieXyz.cs
rename to src/ImageSharp/Colors/Spaces/CieXyz.cs
index 16d5cb827..49396d3f6 100644
--- a/src/ImageSharp/Colors/Colorspaces/CieXyz.cs
+++ b/src/ImageSharp/Colors/Spaces/CieXyz.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@@ -20,15 +20,10 @@ namespace ImageSharp
///
public static readonly CieXyz Empty = default(CieXyz);
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001f;
-
///
/// The backing vector for SIMD support.
///
- private Vector3 backingVector;
+ private readonly Vector3 backingVector;
///
/// Initializes a new instance of the struct.
@@ -129,7 +124,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- return GetHashCode(this);
+ return this.backingVector.GetHashCode();
}
///
@@ -157,7 +152,7 @@ namespace ImageSharp
///
public bool Equals(CieXyz other)
{
- return this.AlmostEquals(other, Epsilon);
+ return this.AlmostEquals(other, Constants.Epsilon);
}
///
@@ -169,16 +164,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(CieXyz color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageSharp/Colors/Colorspaces/Cmyk.cs b/src/ImageSharp/Colors/Spaces/Cmyk.cs
similarity index 87%
rename from src/ImageSharp/Colors/Colorspaces/Cmyk.cs
rename to src/ImageSharp/Colors/Spaces/Cmyk.cs
index 15bea263c..190d73598 100644
--- a/src/ImageSharp/Colors/Colorspaces/Cmyk.cs
+++ b/src/ImageSharp/Colors/Spaces/Cmyk.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@@ -19,15 +19,10 @@ namespace ImageSharp
///
public static readonly Cmyk Empty = default(Cmyk);
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001f;
-
///
/// The backing vector for SIMD support.
///
- private Vector4 backingVector;
+ private readonly Vector4 backingVector;
///
/// Initializes a new instance of the struct.
@@ -90,7 +85,7 @@ namespace ImageSharp
float k = Math.Min(c, Math.Min(m, y));
- if (Math.Abs(k - 1.0f) <= Epsilon)
+ if (Math.Abs(k - 1.0f) <= Constants.Epsilon)
{
return new Cmyk(0, 0, 0, 1);
}
@@ -139,7 +134,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- return GetHashCode(this);
+ return this.backingVector.GetHashCode();
}
///
@@ -167,7 +162,7 @@ namespace ImageSharp
///
public bool Equals(Cmyk other)
{
- return this.AlmostEquals(other, Epsilon);
+ return this.AlmostEquals(other, Constants.Epsilon);
}
///
@@ -180,16 +175,5 @@ namespace ImageSharp
&& result.Z < precision
&& result.W < precision;
}
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(Cmyk color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageSharp/Colors/Colorspaces/Hsl.cs b/src/ImageSharp/Colors/Spaces/Hsl.cs
similarity index 85%
rename from src/ImageSharp/Colors/Colorspaces/Hsl.cs
rename to src/ImageSharp/Colors/Spaces/Hsl.cs
index 1cc54ec8c..2cb02107b 100644
--- a/src/ImageSharp/Colors/Colorspaces/Hsl.cs
+++ b/src/ImageSharp/Colors/Spaces/Hsl.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@@ -19,15 +19,10 @@ namespace ImageSharp
///
public static readonly Hsl Empty = default(Hsl);
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001F;
-
///
/// The backing vector for SIMD support.
///
- private Vector3 backingVector;
+ private readonly Vector3 backingVector;
///
/// Initializes a new instance of the struct.
@@ -85,20 +80,20 @@ namespace ImageSharp
float s = 0;
float l = (max + min) / 2;
- if (Math.Abs(chroma) < Epsilon)
+ if (Math.Abs(chroma) < Constants.Epsilon)
{
return new Hsl(0, s, l);
}
- if (Math.Abs(r - max) < Epsilon)
+ if (Math.Abs(r - max) < Constants.Epsilon)
{
h = (g - b) / chroma;
}
- else if (Math.Abs(g - max) < Epsilon)
+ else if (Math.Abs(g - max) < Constants.Epsilon)
{
h = 2 + ((b - r) / chroma);
}
- else if (Math.Abs(b - max) < Epsilon)
+ else if (Math.Abs(b - max) < Constants.Epsilon)
{
h = 4 + ((r - g) / chroma);
}
@@ -158,7 +153,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- return GetHashCode(this);
+ return this.backingVector.GetHashCode();
}
///
@@ -186,7 +181,7 @@ namespace ImageSharp
///
public bool Equals(Hsl other)
{
- return this.AlmostEquals(other, Epsilon);
+ return this.AlmostEquals(other, Constants.Epsilon);
}
///
@@ -198,16 +193,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(Hsl color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageSharp/Colors/Colorspaces/Hsv.cs b/src/ImageSharp/Colors/Spaces/Hsv.cs
similarity index 85%
rename from src/ImageSharp/Colors/Colorspaces/Hsv.cs
rename to src/ImageSharp/Colors/Spaces/Hsv.cs
index b55e07e6b..8f7ebbdc7 100644
--- a/src/ImageSharp/Colors/Colorspaces/Hsv.cs
+++ b/src/ImageSharp/Colors/Spaces/Hsv.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@@ -19,15 +19,10 @@ namespace ImageSharp
///
public static readonly Hsv Empty = default(Hsv);
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001F;
-
///
/// The backing vector for SIMD support.
///
- private Vector3 backingVector;
+ private readonly Vector3 backingVector;
///
/// Initializes a new instance of the struct.
@@ -85,20 +80,20 @@ namespace ImageSharp
float s = 0;
float v = max;
- if (Math.Abs(chroma) < Epsilon)
+ if (Math.Abs(chroma) < Constants.Epsilon)
{
return new Hsv(0, s, v);
}
- if (Math.Abs(r - max) < Epsilon)
+ if (Math.Abs(r - max) < Constants.Epsilon)
{
h = (g - b) / chroma;
}
- else if (Math.Abs(g - max) < Epsilon)
+ else if (Math.Abs(g - max) < Constants.Epsilon)
{
h = 2 + ((b - r) / chroma);
}
- else if (Math.Abs(b - max) < Epsilon)
+ else if (Math.Abs(b - max) < Constants.Epsilon)
{
h = 4 + ((r - g) / chroma);
}
@@ -151,7 +146,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- return GetHashCode(this);
+ return this.backingVector.GetHashCode();
}
///
@@ -179,7 +174,7 @@ namespace ImageSharp
///
public bool Equals(Hsv other)
{
- return this.AlmostEquals(other, Epsilon);
+ return this.AlmostEquals(other, Constants.Epsilon);
}
///
@@ -191,16 +186,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
-
- ///
- /// Returns the hash code for this instance.
- ///
- ///
- /// The instance of to return the hash code for.
- ///
- ///
- /// A 32-bit signed integer that is the hash code for this instance.
- ///
- private static int GetHashCode(Hsv color) => color.backingVector.GetHashCode();
}
}
diff --git a/src/ImageSharp/Colors/Colorspaces/IAlmostEquatable.cs b/src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs
similarity index 97%
rename from src/ImageSharp/Colors/Colorspaces/IAlmostEquatable.cs
rename to src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs
index 1163657ed..a2183d396 100644
--- a/src/ImageSharp/Colors/Colorspaces/IAlmostEquatable.cs
+++ b/src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs
@@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
diff --git a/src/ImageSharp/Colors/Colorspaces/YCbCr.cs b/src/ImageSharp/Colors/Spaces/YCbCr.cs
similarity index 85%
rename from src/ImageSharp/Colors/Colorspaces/YCbCr.cs
rename to src/ImageSharp/Colors/Spaces/YCbCr.cs
index c9a0872c5..83bddff7d 100644
--- a/src/ImageSharp/Colors/Colorspaces/YCbCr.cs
+++ b/src/ImageSharp/Colors/Spaces/YCbCr.cs
@@ -3,10 +3,11 @@
// Licensed under the Apache License, Version 2.0.
//
-namespace ImageSharp
+namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
+ using System.Numerics;
///
/// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the full range standard used in digital imaging systems.
@@ -19,6 +20,16 @@ namespace ImageSharp
///
public static readonly YCbCr Empty = default(YCbCr);
+ ///
+ /// Vector which is used in clamping to the max value
+ ///
+ private static readonly Vector3 VectorMax = new Vector3(255);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private readonly Vector3 backingVector;
+
///
/// Initializes a new instance of the struct.
///
@@ -28,28 +39,26 @@ namespace ImageSharp
public YCbCr(byte y, byte cb, byte cr)
: this()
{
- this.Y = y;
- this.Cb = cb;
- this.Cr = cr;
+ this.backingVector = Vector3.Clamp(new Vector3(y, cb, cr), Vector3.Zero, VectorMax);
}
///
/// Gets the Y luminance component.
/// A value ranging between 0 and 255.
///
- public byte Y { get; }
+ public float Y => this.backingVector.X;
///
/// Gets the Cb chroma component.
/// A value ranging between 0 and 255.
///
- public byte Cb { get; }
+ public float Cb => this.backingVector.Y;
///
/// Gets the Cr chroma component.
/// A value ranging between 0 and 255.
///
- public byte Cr { get; }
+ public float Cr => this.backingVector.Z;
///
/// Gets a value indicating whether this is empty.
@@ -117,13 +126,7 @@ namespace ImageSharp
///
public override int GetHashCode()
{
- unchecked
- {
- int hashCode = this.Y.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Cb.GetHashCode();
- hashCode = (hashCode * 397) ^ this.Cr.GetHashCode();
- return hashCode;
- }
+ return this.backingVector.GetHashCode();
}
///
@@ -151,7 +154,7 @@ namespace ImageSharp
///
public bool Equals(YCbCr other)
{
- return this.Y == other.Y && this.Cb == other.Cb && this.Cr == other.Cr;
+ return this.backingVector.Equals(other.backingVector);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Vector4BlendTransforms.cs b/src/ImageSharp/Colors/Vector4BlendTransforms.cs
index 870d65388..2fa6aad4b 100644
--- a/src/ImageSharp/Colors/Vector4BlendTransforms.cs
+++ b/src/ImageSharp/Colors/Vector4BlendTransforms.cs
@@ -14,11 +14,6 @@ namespace ImageSharp
///
public class Vector4BlendTransforms
{
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.0001F;
-
///
/// The blending formula simply selects the source vector.
///
@@ -203,13 +198,13 @@ namespace ImageSharp
amount = amount.Clamp(0, 1);
// Santize on zero alpha
- if (Math.Abs(backdrop.W) < Epsilon)
+ if (Math.Abs(backdrop.W) < Constants.Epsilon)
{
source.W *= amount;
return source;
}
- if (Math.Abs(source.W) < Epsilon)
+ if (Math.Abs(source.W) < Constants.Epsilon)
{
return backdrop;
}
@@ -266,7 +261,7 @@ namespace ImageSharp
///
private static float BlendDodge(float b, float s)
{
- return Math.Abs(s - 1F) < Epsilon ? s : Math.Min(b / (1F - s), 1F);
+ return Math.Abs(s - 1F) < Constants.Epsilon ? s : Math.Min(b / (1F - s), 1F);
}
///
@@ -279,7 +274,7 @@ namespace ImageSharp
///
private static float BlendBurn(float b, float s)
{
- return Math.Abs(s) < Epsilon ? s : Math.Max(1F - ((1F - b) / s), 0F);
+ return Math.Abs(s) < Constants.Epsilon ? s : Math.Max(1F - ((1F - b) / s), 0F);
}
///
diff --git a/src/ImageSharp/Common/Constants.cs b/src/ImageSharp/Common/Constants.cs
new file mode 100644
index 000000000..cf43951bc
--- /dev/null
+++ b/src/ImageSharp/Common/Constants.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Common constants used throughout the project
+ ///
+ internal static class Constants
+ {
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ public static readonly float Epsilon = 0.001f;
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index 99212346a..7455b542d 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -91,9 +91,7 @@ namespace ImageSharp
///
public static float SinC(float x)
{
- const float Epsilon = .00001F;
-
- if (Math.Abs(x) > Epsilon)
+ if (Math.Abs(x) > Constants.Epsilon)
{
x *= (float)Math.PI;
return Clean((float)Math.Sin(x) / x);
@@ -166,7 +164,6 @@ namespace ImageSharp
public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
where TColor : struct, IPackedPixel, IEquatable
{
- const float Epsilon = .00001f;
int width = bitmap.Width;
int height = bitmap.Height;
Point topLeft = default(Point);
@@ -178,19 +175,19 @@ namespace ImageSharp
switch (channel)
{
case RgbaComponent.R:
- delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().X - b) > Epsilon;
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon;
break;
case RgbaComponent.G:
- delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Y - b) > Epsilon;
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon;
break;
case RgbaComponent.B:
- delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Z - b) > Epsilon;
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon;
break;
default:
- delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().W - b) > Epsilon;
+ delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon;
break;
}
@@ -278,9 +275,7 @@ namespace ImageSharp
/// .
private static float Clean(float x)
{
- const float Epsilon = .00001F;
-
- if (Math.Abs(x) < Epsilon)
+ if (Math.Abs(x) < Constants.Epsilon)
{
return 0F;
}
diff --git a/src/ImageSharp/Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp/Drawing/Processors/DrawPathProcessor.cs
index 54ebc28ef..dab0baa5a 100644
--- a/src/ImageSharp/Drawing/Processors/DrawPathProcessor.cs
+++ b/src/ImageSharp/Drawing/Processors/DrawPathProcessor.cs
@@ -9,7 +9,6 @@ namespace ImageSharp.Drawing.Processors
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
-
using ImageSharp.Processors;
using Paths;
using Pens;
@@ -27,7 +26,6 @@ namespace ImageSharp.Drawing.Processors
{
private const float AntialiasFactor = 1f;
private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor
- private const float Epsilon = 0.001f;
private readonly IPen pen;
private readonly IPath[] paths;
@@ -138,7 +136,7 @@ namespace ImageSharp.Drawing.Processors
var opacity = this.Opacity(color.DistanceFromElement);
- if (opacity > Epsilon)
+ if (opacity > Constants.Epsilon)
{
int offsetColorX = x - minX;
diff --git a/src/ImageSharp/Drawing/Processors/FillShapeProcessor.cs b/src/ImageSharp/Drawing/Processors/FillShapeProcessor.cs
index df5cec71c..7da2e041a 100644
--- a/src/ImageSharp/Drawing/Processors/FillShapeProcessor.cs
+++ b/src/ImageSharp/Drawing/Processors/FillShapeProcessor.cs
@@ -21,8 +21,6 @@ namespace ImageSharp.Drawing.Processors
public class FillShapeProcessor : ImageFilteringProcessor
where TColor : struct, IPackedPixel, IEquatable
{
- private const float Epsilon = 0.001f;
-
private const float AntialiasFactor = 1f;
private const int DrawPadding = 1;
private readonly IBrush fillColor;
@@ -95,7 +93,7 @@ namespace ImageSharp.Drawing.Processors
float dist = this.poly.Distance(currentPoint);
float opacity = this.Opacity(dist);
- if (opacity > Epsilon)
+ if (opacity > Constants.Epsilon)
{
Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4();
Vector4 sourceVector = applicator.GetColor(currentPoint).ToVector4();
diff --git a/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
index 122c5a0ff..ab6fa2424 100644
--- a/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
@@ -16,11 +16,6 @@ namespace ImageSharp.Processors
public class BackgroundColorProcessor : ImageFilteringProcessor
where TColor : struct, IPackedPixel, IEquatable
{
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.001f;
-
///
/// Initializes a new instance of the class.
///
@@ -82,7 +77,7 @@ namespace ImageSharp.Processors
color = Vector4BlendTransforms.PremultipliedLerp(backgroundColor, color, .5F);
}
- if (Math.Abs(a) < Epsilon)
+ if (Math.Abs(a) < Constants.Epsilon)
{
color = backgroundColor;
}
diff --git a/src/ImageSharp/Filters/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Filters/Processors/Transforms/RotateProcessor.cs
index 33e5b6470..e81cd7e57 100644
--- a/src/ImageSharp/Filters/Processors/Transforms/RotateProcessor.cs
+++ b/src/ImageSharp/Filters/Processors/Transforms/RotateProcessor.cs
@@ -70,9 +70,7 @@ namespace ImageSharp.Processors
///
protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle)
{
- const float Epsilon = .0001F;
-
- if (Math.Abs(this.Angle) < Epsilon || Math.Abs(this.Angle - 90) < Epsilon || Math.Abs(this.Angle - 180) < Epsilon || Math.Abs(this.Angle - 270) < Epsilon)
+ if (Math.Abs(this.Angle) < Constants.Epsilon || Math.Abs(this.Angle - 90) < Constants.Epsilon || Math.Abs(this.Angle - 180) < Constants.Epsilon || Math.Abs(this.Angle - 270) < Constants.Epsilon)
{
return;
}
@@ -91,26 +89,25 @@ namespace ImageSharp.Processors
/// The
private bool OptimizedApply(ImageBase source)
{
- const float Epsilon = .0001F;
- if (Math.Abs(this.Angle) < Epsilon)
+ if (Math.Abs(this.Angle) < Constants.Epsilon)
{
// No need to do anything so return.
return true;
}
- if (Math.Abs(this.Angle - 90) < Epsilon)
+ if (Math.Abs(this.Angle - 90) < Constants.Epsilon)
{
this.Rotate90(source);
return true;
}
- if (Math.Abs(this.Angle - 180) < Epsilon)
+ if (Math.Abs(this.Angle - 180) < Constants.Epsilon)
{
this.Rotate180(source);
return true;
}
- if (Math.Abs(this.Angle - 270) < Epsilon)
+ if (Math.Abs(this.Angle - 270) < Constants.Epsilon)
{
this.Rotate270(source);
return true;
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 981d6c097..562417601 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -275,7 +275,7 @@ namespace ImageSharp.Formats
const int ComponentCount = 2;
TColor color = default(TColor);
- using (PixelArea row = new PixelArea(width, ComponentOrder.XYZ))
+ using (PixelArea row = new PixelArea(width, ComponentOrder.Xyz))
{
for (int y = 0; y < height; y++)
{
@@ -312,7 +312,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable
{
int padding = CalculatePadding(width, 3);
- using (PixelArea row = new PixelArea(width, ComponentOrder.ZYX, padding))
+ using (PixelArea row = new PixelArea(width, ComponentOrder.Zyx, padding))
{
for (int y = 0; y < height; y++)
{
@@ -336,7 +336,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable
{
int padding = CalculatePadding(width, 4);
- using (PixelArea row = new PixelArea(width, ComponentOrder.ZYXW, padding))
+ using (PixelArea row = new PixelArea(width, ComponentOrder.Zyxw, padding))
{
for (int y = 0; y < height; y++)
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index 8b7d79156..20c899a18 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -150,12 +150,12 @@ namespace ImageSharp.Formats
private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
{
- using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.ZYXW, this.padding))
+ using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
pixels.CopyTo(row, y);
- writer.Write(row.Bytes);
+ writer.Write(row.Bytes, 0, row.Length);
}
}
}
@@ -169,12 +169,12 @@ namespace ImageSharp.Formats
private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels)
where TColor : struct, IPackedPixel, IEquatable
{
- using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.ZYX, this.padding))
+ using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding))
{
for (int y = pixels.Height - 1; y >= 0; y--)
{
pixels.CopyTo(row, y);
- writer.Write(row.Bytes);
+ writer.Write(row.Bytes, 0, row.Length);
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index c3eb075f3..2be8aed37 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -451,7 +451,7 @@ namespace ImageSharp.Formats
}
else
{
- using (PixelArea emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.XYZW))
+ using (PixelArea emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
{
using (PixelAccessor pixelAccessor = frame.Lock())
{
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 6b99423ae..8a851fd68 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -129,10 +129,10 @@ namespace ImageSharp.Formats
bool hasEmpty = false;
// Some images may have more than one quantized pixel returned with an alpha value of zero
- // (No idea why?!) so we should always ignore if we have empty pixels present.
+ // so we should always ignore if we have empty pixels present.
for (int i = 0; i < quantized.Palette.Length; i++)
{
- quantized.Palette[i].ToBytes(this.buffer, 0, ComponentOrder.XYZW);
+ quantized.Palette[i].ToXyzwBytes(this.buffer, 0);
if (!hasEmpty)
{
@@ -282,6 +282,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable
{
writer.Write(GifConstants.ImageDescriptorLabel); // 2c
+
// TODO: Can we capture this?
writer.Write((ushort)0); // Left position
writer.Write((ushort)0); // Top position
@@ -318,7 +319,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
- image.Palette[i].ToBytes(this.buffer, 0, ComponentOrder.XYZ);
+ image.Palette[i].ToXyzBytes(this.buffer, 0);
colorTable[offset] = this.buffer[0];
colorTable[offset + 1] = this.buffer[1];
colorTable[offset + 2] = this.buffer[2];
diff --git a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
index 723ccd6b8..63d8b0e54 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
@@ -373,5 +373,12 @@ namespace ImageSharp.Formats.Jpg
}
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void Copy(Block8x8F* dest, Block8x8F* source)
+ {
+ *dest = *source;
+ //Unsafe.CopyBlock(dest, source, (uint)sizeof(Block8x8F));
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/GrayImage.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/GrayImage.cs
deleted file mode 100644
index caa30e62d..000000000
--- a/src/ImageSharp/Formats/Jpg/Components/Decoder/GrayImage.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp.Formats.Jpg
-{
- ///
- /// Represents a grayscale image
- ///
- internal class GrayImage
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The width.
- /// The height.
- public GrayImage(int width, int height)
- {
- this.Width = width;
- this.Height = height;
- this.Pixels = new byte[width * height];
- this.Stride = width;
- this.Offset = 0;
- }
-
- ///
- /// Prevents a default instance of the class from being created.
- ///
- private GrayImage()
- {
- }
-
- ///
- /// Gets or sets the pixels.
- ///
- public byte[] Pixels { get; set; }
-
- ///
- /// Gets or sets the stride.
- ///
- public int Stride { get; set; }
-
- ///
- /// Gets or sets the horizontal position.
- ///
- public int X { get; set; }
-
- ///
- /// Gets or sets the vertical position.
- ///
- public int Y { get; set; }
-
- ///
- /// Gets or sets the width.
- ///
- public int Width { get; set; }
-
- ///
- /// Gets or sets the height.
- ///
- public int Height { get; set; }
-
- ///
- /// Gets or sets the offset
- ///
- public int Offset { get; set; }
-
- ///
- /// Gets an image made up of a subset of the originals pixels.
- ///
- /// The x-coordinate of the image.
- /// The y-coordinate of the image.
- /// The width.
- /// The height.
- ///
- /// The .
- ///
- public GrayImage Subimage(int x, int y, int width, int height)
- {
- return new GrayImage
- {
- Width = width,
- Height = height,
- Pixels = this.Pixels,
- Stride = this.Stride,
- Offset = (y * this.Stride) + x
- };
- }
-
- ///
- /// Gets the row offset at the given position
- ///
- /// The y-coordinate of the image.
- /// The
- public int GetRowOffset(int y)
- {
- return this.Offset + (y * this.Stride);
- }
- }
-}
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
index a148cc558..bdf3468e6 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
@@ -8,68 +8,118 @@ namespace ImageSharp.Formats.Jpg
using System.Buffers;
///
- /// Represents a Huffman tree
+ /// Represents a Huffman tree
///
internal struct HuffmanTree : IDisposable
{
///
- /// Gets or sets the number of codes in the tree.
+ /// The maximum (inclusive) number of codes in a Huffman tree.
+ ///
+ public const int MaxNCodes = 256;
+
+ ///
+ /// The maximum (inclusive) number of bits in a Huffman code.
+ ///
+ public const int MaxCodeLength = 16;
+
+ ///
+ /// The maximum number of Huffman table classes
+ ///
+ public const int MaxTc = 1;
+
+ ///
+ /// The maximum number of Huffman table identifiers
+ ///
+ public const int MaxTh = 3;
+
+ ///
+ /// Row size of the Huffman table
+ ///
+ public const int ThRowSize = MaxTh + 1;
+
+ ///
+ /// Number of Hufman Trees in the Huffman table
+ ///
+ public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1);
+
+ ///
+ /// The log-2 size of the Huffman decoder's look-up table.
+ ///
+ public const int LutSize = 8;
+
+ ///
+ /// Gets or sets the number of codes in the tree.
///
public int Length;
///
- /// Gets the look-up table for the next LutSize bits in the bit-stream.
- /// The high 8 bits of the uint16 are the encoded value. The low 8 bits
- /// are 1 plus the code length, or 0 if the value is too large to fit in
- /// lutSize bits.
+ /// Gets the look-up table for the next LutSize bits in the bit-stream.
+ /// The high 8 bits of the uint16 are the encoded value. The low 8 bits
+ /// are 1 plus the code length, or 0 if the value is too large to fit in
+ /// lutSize bits.
///
public ushort[] Lut;
///
- /// Gets the the decoded values, sorted by their encoding.
+ /// Gets the the decoded values, sorted by their encoding.
///
public byte[] Values;
///
- /// Gets the array of minimum codes.
- /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length.
+ /// Gets the array of minimum codes.
+ /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length.
///
public int[] MinCodes;
///
- /// Gets the array of maximum codes.
- /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length.
+ /// Gets the array of maximum codes.
+ /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length.
///
public int[] MaxCodes;
///
- /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i].
+ /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i].
///
public int[] Indices;
- private static readonly ArrayPool UshortBuffer = ArrayPool.Create(1 << JpegDecoderCore.LutSize, 50);
+ private static readonly ArrayPool UshortBuffer = ArrayPool.Create(1 << LutSize, 50);
- private static readonly ArrayPool ByteBuffer = ArrayPool.Create(JpegDecoderCore.MaxNCodes, 50);
+ private static readonly ArrayPool ByteBuffer = ArrayPool.Create(MaxNCodes, 50);
- private static readonly ArrayPool IntBuffer = ArrayPool.Create(JpegDecoderCore.MaxCodeLength, 50);
+ private static readonly ArrayPool IntBuffer = ArrayPool.Create(MaxCodeLength, 50);
///
- /// Initializes the Huffman tree
+ /// Creates and initializes an array of instances of size
///
- /// Lut size
- /// Max N codes
- /// Max code length
- public void Init(int lutSize, int maxNCodes, int maxCodeLength)
+ /// An array of instances representing the Huffman tables
+ public static HuffmanTree[] CreateHuffmanTrees()
{
- this.Lut = UshortBuffer.Rent(1 << lutSize);
- this.Values = ByteBuffer.Rent(maxNCodes);
- this.MinCodes = IntBuffer.Rent(maxCodeLength);
- this.MaxCodes = IntBuffer.Rent(maxCodeLength);
- this.Indices = IntBuffer.Rent(maxCodeLength);
+ HuffmanTree[] result = new HuffmanTree[NumberOfTrees];
+ for (int i = 0; i < MaxTc + 1; i++)
+ {
+ for (int j = 0; j < MaxTh + 1; j++)
+ {
+ result[(i * ThRowSize) + j].Init();
+ }
+ }
+
+ return result;
}
///
- /// Disposes the underlying buffers
+ /// Initializes the Huffman tree
+ ///
+ private void Init()
+ {
+ this.Lut = UshortBuffer.Rent(1 << LutSize);
+ this.Values = ByteBuffer.Rent(MaxNCodes);
+ this.MinCodes = IntBuffer.Rent(MaxCodeLength);
+ this.MaxCodes = IntBuffer.Rent(MaxCodeLength);
+ this.Indices = IntBuffer.Rent(MaxCodeLength);
+ }
+
+ ///
+ /// Disposes the underlying buffers
///
public void Dispose()
{
@@ -79,5 +129,102 @@ namespace ImageSharp.Formats.Jpg
IntBuffer.Return(this.MaxCodes, true);
IntBuffer.Return(this.Indices, true);
}
+
+ ///
+ /// Internal part of the DHT processor, whatever does it mean
+ ///
+ /// The decoder instance
+ /// The temporal buffer that holds the data that has been read from the Jpeg stream
+ /// Remaining bits
+ internal void ProcessDefineHuffmanTablesMarkerLoop(
+ JpegDecoderCore decoder,
+ byte[] defineHuffmanTablesData,
+ ref int remaining)
+ {
+ // Read nCodes and huffman.Valuess (and derive h.Length).
+ // nCodes[i] is the number of codes with code length i.
+ // h.Length is the total number of codes.
+ this.Length = 0;
+
+ int[] ncodes = new int[MaxCodeLength];
+ for (int i = 0; i < ncodes.Length; i++)
+ {
+ ncodes[i] = defineHuffmanTablesData[i + 1];
+ this.Length += ncodes[i];
+ }
+
+ if (this.Length == 0)
+ {
+ throw new ImageFormatException("Huffman table has zero length");
+ }
+
+ if (this.Length > MaxNCodes)
+ {
+ throw new ImageFormatException("Huffman table has excessive length");
+ }
+
+ remaining -= this.Length + 17;
+ if (remaining < 0)
+ {
+ throw new ImageFormatException("DHT has wrong length");
+ }
+
+ decoder.ReadFull(this.Values, 0, this.Length);
+
+ // Derive the look-up table.
+ for (int i = 0; i < this.Lut.Length; i++)
+ {
+ this.Lut[i] = 0;
+ }
+
+ uint x = 0, code = 0;
+
+ for (int i = 0; i < LutSize; i++)
+ {
+ code <<= 1;
+
+ for (int j = 0; j < ncodes[i]; j++)
+ {
+ // The codeLength is 1+i, so shift code by 8-(1+i) to
+ // calculate the high bits for every 8-bit sequence
+ // whose codeLength's high bits matches code.
+ // The high 8 bits of lutValue are the encoded value.
+ // The low 8 bits are 1 plus the codeLength.
+ byte base2 = (byte)(code << (7 - i));
+ ushort lutValue = (ushort)((this.Values[x] << 8) | (2 + i));
+
+ for (int k = 0; k < 1 << (7 - i); k++)
+ {
+ this.Lut[base2 | k] = lutValue;
+ }
+
+ code++;
+ x++;
+ }
+ }
+
+ // Derive minCodes, maxCodes, and indices.
+ int c = 0, index = 0;
+ for (int i = 0; i < ncodes.Length; i++)
+ {
+ int nc = ncodes[i];
+ if (nc == 0)
+ {
+ this.MinCodes[i] = -1;
+ this.MaxCodes[i] = -1;
+ this.Indices[i] = -1;
+ }
+ else
+ {
+ this.MinCodes[i] = c;
+ this.MaxCodes[i] = c + nc - 1;
+ this.Indices[i] = index;
+ c += nc;
+ index += nc;
+ }
+
+ c <<= 1;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs
new file mode 100644
index 000000000..00e2b0e13
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs
@@ -0,0 +1,132 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+namespace ImageSharp.Formats.Jpg
+{
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Represents an area of a Jpeg subimage (channel)
+ ///
+ internal struct JpegPixelArea
+ {
+ ///
+ /// Initializes a new instance of the struct from existing data.
+ ///
+ /// The pixel array
+ /// The stride
+ /// The offset
+ public JpegPixelArea(byte[] pixels, int striede, int offset)
+ {
+ this.Stride = striede;
+ this.Pixels = pixels;
+ this.Offset = offset;
+ }
+
+ ///
+ /// Gets the pixels.
+ ///
+ public byte[] Pixels { get; private set; }
+
+ ///
+ /// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea))
+ ///
+ public bool IsInitialized => this.Pixels != null;
+
+ ///
+ /// Gets or the stride.
+ ///
+ public int Stride { get; }
+
+ ///
+ /// Gets or the offset.
+ ///
+ public int Offset { get; }
+
+ ///
+ /// Gets a of bytes to the pixel area
+ ///
+ public MutableSpan Span => new MutableSpan(this.Pixels, this.Offset);
+
+ ///
+ /// Returns the pixel at (x, y)
+ ///
+ /// The x index
+ /// The y index
+ /// The pixel value
+ public byte this[int x, int y]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return this.Pixels[(y * this.Stride) + x];
+ }
+ }
+
+ ///
+ /// Creates a new instance of the struct.
+ /// Pixel array will be taken from a pool, this instance will be the owner of it's pixel data, therefore
+ /// should be called when the instance is no longer needed.
+ ///
+ /// The width.
+ /// The height.
+ /// A with pooled data
+ public static JpegPixelArea CreatePooled(int width, int height)
+ {
+ int size = width * height;
+ var pixels = CleanPooler.RentCleanArray(size);
+ return new JpegPixelArea(pixels, width, 0);
+ }
+
+ ///
+ /// Returns to the pool
+ ///
+ public void ReturnPooled()
+ {
+ if (this.Pixels == null)
+ {
+ return;
+ }
+
+ CleanPooler.ReturnArray(this.Pixels);
+ this.Pixels = null;
+ }
+
+ ///
+ /// Gets the subarea that belongs to the Block8x8 defined by block indices
+ ///
+ /// The block X index
+ /// The block Y index
+ /// The subarea offseted by block indices
+ public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by)
+ {
+ int offset = this.Offset + (8 * ((by * this.Stride) + bx));
+ return new JpegPixelArea(this.Pixels, this.Stride, offset);
+ }
+
+ ///
+ /// Gets the row offset at the given position
+ ///
+ /// The y-coordinate of the image.
+ /// The
+ public int GetRowOffset(int y)
+ {
+ return this.Offset + (y * this.Stride);
+ }
+
+ ///
+ /// Load values to the pixel area from the given .
+ /// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to
+ /// values
+ ///
+ /// The block holding the color values
+ /// Temporal block provided by the caller
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp)
+ {
+ // Level shift by +128, clip to [0, 255], and write to dst.
+ block->CopyColorsTo(new MutableSpan(this.Pixels, this.Offset), this.Stride, temp);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs
new file mode 100644
index 000000000..162874f02
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs
@@ -0,0 +1,653 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Formats.Jpg
+{
+ using System;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// Encapsulates the impementation of Jpeg SOS decoder.
+ /// See JpegScanDecoder.md!
+ ///
+ internal unsafe struct JpegScanDecoder
+ {
+ ///
+ /// The AC table index
+ ///
+ internal const int AcTableIndex = 1;
+
+ ///
+ /// The DC table index
+ ///
+ internal const int DcTableIndex = 0;
+
+ ///
+ /// Holds the "large" data blocks needed for computations
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ComponentData
+ {
+ public Block8x8F Block;
+
+ public Block8x8F Temp1;
+
+ public Block8x8F Temp2;
+
+ public Block8x8F QuantiazationTable;
+
+ public UnzigData Unzig;
+
+ public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents];
+
+ public fixed int Dc [JpegDecoderCore.MaxComponents];
+
+ public static ComponentData Create()
+ {
+ ComponentData data = default(ComponentData);
+ data.Unzig = UnzigData.Create();
+ return data;
+ }
+ }
+
+ ///
+ /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of
+ ///
+ public struct ComponentPointers
+ {
+ public Block8x8F* Block;
+
+ public Block8x8F* Temp1;
+
+ public Block8x8F* Temp2;
+
+ public Block8x8F* QuantiazationTable;
+
+ public int* Unzig;
+
+ public Scan* Scan;
+
+ public int* Dc;
+
+ public ComponentPointers(ComponentData* basePtr)
+ {
+ this.Block = &basePtr->Block;
+ this.Temp1 = &basePtr->Temp1;
+ this.Temp2 = &basePtr->Temp2;
+ this.QuantiazationTable = &basePtr->QuantiazationTable;
+ this.Unzig = basePtr->Unzig.Data;
+ this.Scan = (Scan*)basePtr->ScanData;
+ this.Dc = basePtr->Dc;
+ }
+ }
+
+ private void ResetDc()
+ {
+ Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents);
+ }
+
+
+ // bx and by are the
+ // blocks: the third block in the first row has (bx, by) = (2, 0).
+
+ ///
+ /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
+ ///
+ private int bx;
+
+ ///
+ /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
+ ///
+ private int by;
+
+ // zigStart and zigEnd are the spectral selection bounds.
+ // ah and al are the successive approximation high and low values.
+ // The spec calls these values Ss, Se, Ah and Al.
+ // For progressive JPEGs, these are the two more-or-less independent
+ // aspects of progression. Spectral selection progression is when not
+ // all of a block's 64 DCT coefficients are transmitted in one pass.
+ // For example, three passes could transmit coefficient 0 (the DC
+ // component), coefficients 1-5, and coefficients 6-63, in zig-zag
+ // order. Successive approximation is when not all of the bits of a
+ // band of coefficients are transmitted in one pass. For example,
+ // three passes could transmit the 6 most significant bits, followed
+ // by the second-least significant bit, followed by the least
+ // significant bit.
+ // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
+
+ private int zigStart;
+
+ private int zigEnd;
+
+ private int ah;
+
+ private int al;
+
+ // XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
+
+ public int XNumberOfMCUs;
+
+ public int YNumberOfMCUs;
+
+ private int scanComponentCount;
+
+ private ComponentData Data;
+
+ private ComponentPointers Pointers;
+
+ public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining)
+ {
+ p->Data = ComponentData.Create();
+ p->Pointers = new ComponentPointers(&p->Data);
+ p->InitImpl(decoder, remaining);
+ }
+
+ private void InitImpl(JpegDecoderCore decoder, int remaining)
+ {
+ if (decoder.ComponentCount == 0)
+ {
+ throw new ImageFormatException("Missing SOF marker");
+ }
+
+ if (remaining < 6 || 4 + (2 * decoder.ComponentCount) < remaining || remaining % 2 != 0)
+ {
+ throw new ImageFormatException("SOS has wrong length");
+ }
+
+ decoder.ReadFull(decoder.Temp, 0, remaining);
+ this.scanComponentCount = decoder.Temp[0];
+
+ int scanComponentCountX2 = 2 * this.scanComponentCount;
+ if (remaining != 4 + scanComponentCountX2)
+ {
+ throw new ImageFormatException("SOS length inconsistent with number of components");
+ }
+
+ int totalHv = 0;
+
+ for (int i = 0; i < this.scanComponentCount; i++)
+ {
+ this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv);
+ }
+ // Section B.2.3 states that if there is more than one component then the
+ // total H*V values in a scan must be <= 10.
+ if (decoder.ComponentCount > 1 && totalHv > 10)
+ {
+ throw new ImageFormatException("Total sampling factors too large.");
+ }
+
+ this.zigEnd = Block8x8F.ScalarCount - 1;
+
+ if (decoder.IsProgressive)
+ {
+ this.zigStart = decoder.Temp[1 + scanComponentCountX2];
+ this.zigEnd = decoder.Temp[2 + scanComponentCountX2];
+ this.ah = decoder.Temp[3 + scanComponentCountX2] >> 4;
+ this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f;
+
+ if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd
+ || this.zigEnd >= Block8x8F.ScalarCount)
+ {
+ throw new ImageFormatException("Bad spectral selection bounds");
+ }
+
+ if (this.zigStart != 0 && this.scanComponentCount != 1)
+ {
+ throw new ImageFormatException("Progressive AC coefficients for more than one component");
+ }
+
+ if (this.ah != 0 && this.ah != this.al + 1)
+ {
+ throw new ImageFormatException("Bad successive approximation values");
+ }
+ }
+
+ // XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
+ int h0 = decoder.ComponentArray[0].HorizontalFactor;
+ int v0 = decoder.ComponentArray[0].VerticalFactor;
+ this.XNumberOfMCUs = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0);
+ this.YNumberOfMCUs = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0);
+
+ if (decoder.IsProgressive)
+ {
+ for (int i = 0; i < this.scanComponentCount; i++)
+ {
+ int compIndex = this.Pointers.Scan[i].Index;
+ if (decoder.ProgCoeffs[compIndex] == null)
+ {
+ int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor
+ * decoder.ComponentArray[compIndex].VerticalFactor;
+
+ decoder.ProgCoeffs[compIndex] = new Block8x8F[size];
+ }
+ }
+ }
+ }
+
+ private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv)
+ {
+ // Component selector.
+ int cs = decoder.Temp[1 + (2 * i)];
+ int compIndex = -1;
+ for (int j = 0; j < decoder.ComponentCount; j++)
+ {
+ // Component compv = ;
+ if (cs == decoder.ComponentArray[j].Identifier)
+ {
+ compIndex = j;
+ }
+ }
+
+ if (compIndex < 0)
+ {
+ throw new ImageFormatException("Unknown component selector");
+ }
+
+ currentScan.Index = (byte)compIndex;
+
+ this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]);
+ }
+
+
+ private void ProcessComponentImpl(
+ JpegDecoderCore decoder,
+ int i,
+ ref Scan currentScan,
+ ref int totalHv,
+ ref Component currentComponent)
+ {
+ // Section B.2.3 states that "the value of Cs_j shall be different from
+ // the values of Cs_1 through Cs_(j-1)". Since we have previously
+ // verified that a frame's component identifiers (C_i values in section
+ // B.2.2) are unique, it suffices to check that the implicit indexes
+ // into comp are unique.
+ for (int j = 0; j < i; j++)
+ {
+ if (currentScan.Index == this.Pointers.Scan[j].Index)
+ {
+ throw new ImageFormatException("Repeated component selector");
+ }
+ }
+
+ totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor;
+
+ currentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4);
+ if (currentScan.DcTableSelector > HuffmanTree.MaxTh)
+ {
+ throw new ImageFormatException("Bad DC table selector value");
+ }
+
+ currentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f);
+ if (currentScan.AcTableSelector > HuffmanTree.MaxTh)
+ {
+ throw new ImageFormatException("Bad AC table selector value");
+ }
+ }
+
+ public void ProcessBlocks(JpegDecoderCore decoder)
+ {
+ int blockCount = 0;
+ int mcu = 0;
+ byte expectedRst = JpegConstants.Markers.RST0;
+
+ for (int my = 0; my < this.YNumberOfMCUs; my++)
+ {
+ for (int mx = 0; mx < this.XNumberOfMCUs; mx++)
+ {
+ for (int i = 0; i < this.scanComponentCount; i++)
+ {
+ int compIndex = this.Pointers.Scan[i].Index;
+ int hi = decoder.ComponentArray[compIndex].HorizontalFactor;
+ int vi = decoder.ComponentArray[compIndex].VerticalFactor;
+
+ for (int j = 0; j < hi * vi; j++)
+ {
+ // The blocks are traversed one MCU at a time. For 4:2:0 chroma
+ // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
+ // For a baseline 32x16 pixel image, the Y blocks visiting order is:
+ // 0 1 4 5
+ // 2 3 6 7
+ // For progressive images, the interleaved scans (those with component count > 1)
+ // are traversed as above, but non-interleaved scans are traversed left
+ // to right, top to bottom:
+ // 0 1 2 3
+ // 4 5 6 7
+ // Only DC scans (zigStart == 0) can be interleave AC scans must have
+ // only one component.
+ // To further complicate matters, for non-interleaved scans, there is no
+ // data for any blocks that are inside the image at the MCU level but
+ // outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
+ // progressive image consists of two 16x16 MCUs. The interleaved scans
+ // will process 8 Y blocks:
+ // 0 1 4 5
+ // 2 3 6 7
+ // The non-interleaved scans will process only 6 Y blocks:
+ // 0 1 2
+ // 3 4 5
+ if (this.scanComponentCount != 1)
+ {
+ this.bx = (hi * mx) + (j % hi);
+ this.by = (vi * my) + (j / hi);
+ }
+ else
+ {
+ int q = this.XNumberOfMCUs * hi;
+ this.bx = blockCount % q;
+ this.by = blockCount / q;
+ blockCount++;
+ if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight)
+ {
+ continue;
+ }
+ }
+
+ int qtIndex = decoder.ComponentArray[compIndex].Selector;
+
+ // TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async.
+
+ this.Data.QuantiazationTable = decoder.QuantizationTables[qtIndex];
+
+ //Load the previous partially decoded coefficients, if applicable.
+ if (decoder.IsProgressive)
+ {
+ int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx;
+ this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex];
+ }
+ else
+ {
+ this.Data.Block.Clear();
+ }
+
+ this.ProcessBlockImpl(decoder, i, compIndex, hi);
+ }
+
+ // for j
+ }
+
+ // for i
+ mcu++;
+
+ if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.XNumberOfMCUs * this.YNumberOfMCUs)
+ {
+ // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
+ // but this one assumes well-formed input, and hence the restart marker follows immediately.
+ decoder.ReadFull(decoder.Temp, 0, 2);
+ if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst)
+ {
+ throw new ImageFormatException("Bad RST marker");
+ }
+
+ expectedRst++;
+ if (expectedRst == JpegConstants.Markers.RST7 + 1)
+ {
+ expectedRst = JpegConstants.Markers.RST0;
+ }
+
+ // Reset the Huffman decoder.
+ decoder.Bits = default(Bits);
+
+ // Reset the DC components, as per section F.2.1.3.1.
+ this.ResetDc();
+
+ // Reset the progressive decoder state, as per section G.1.2.2.
+ decoder.EobRun = 0;
+ }
+ }
+
+ // for mx
+ }
+ }
+
+ ///
+ /// Decodes a successive approximation refinement block, as specified in section G.1.2.
+ ///
+ /// The Huffman tree
+ /// The low transform offset
+ /// The decoder instance
+ private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta)
+ {
+ Block8x8F* b = this.Pointers.Block;
+ // Refining a DC component is trivial.
+ if (this.zigStart == 0)
+ {
+ if (this.zigEnd != 0)
+ {
+ throw new ImageFormatException("Invalid state for zig DC component");
+ }
+
+ bool bit = decoder.DecodeBit();
+ if (bit)
+ {
+ int stuff = (int)Block8x8F.GetScalarAt(b, 0);
+
+ // int stuff = (int)b[0];
+ stuff |= delta;
+
+ // b[0] = stuff;
+ Block8x8F.SetScalarAt(b, 0, stuff);
+ }
+
+ return;
+ }
+
+ // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
+ int zig = this.zigStart;
+ if (decoder.EobRun == 0)
+ {
+ for (; zig <= this.zigEnd; zig++)
+ {
+ bool done = false;
+ int z = 0;
+ byte val = decoder.DecodeHuffman(ref h);
+ int val0 = val >> 4;
+ int val1 = val & 0x0f;
+
+ switch (val1)
+ {
+ case 0:
+ if (val0 != 0x0f)
+ {
+ decoder.EobRun = (ushort)(1 << val0);
+ if (val0 != 0)
+ {
+ decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
+ }
+
+ done = true;
+ }
+
+ break;
+ case 1:
+ z = delta;
+ bool bit = decoder.DecodeBit();
+ if (!bit)
+ {
+ z = -z;
+ }
+
+ break;
+ default:
+ throw new ImageFormatException("Unexpected Huffman code");
+ }
+
+ if (done)
+ {
+ break;
+ }
+
+ zig = this.RefineNonZeroes(decoder, zig, val0, delta);
+ if (zig > this.zigEnd)
+ {
+ throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}");
+ }
+
+ if (z != 0)
+ {
+ // b[Unzig[zig]] = z;
+ Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], z);
+ }
+ }
+ }
+
+ if (decoder.EobRun > 0)
+ {
+ decoder.EobRun--;
+ this.RefineNonZeroes(decoder, zig,-1, delta);
+ }
+ }
+
+ ///
+ /// Refines non-zero entries of b in zig-zag order.
+ /// If >= 0, the first zero entries are skipped over.
+ ///
+ /// The decoder
+ /// The zig-zag start index
+ /// The non-zero entry
+ /// The low transform offset
+ /// The
+ private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta)
+ {
+ var b = this.Pointers.Block;
+ for (; zig <= this.zigEnd; zig++)
+ {
+ int u = this.Pointers.Unzig[zig];
+ float bu = Block8x8F.GetScalarAt(b, u);
+
+ // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
+ if (bu == 0)
+ {
+ if (nz == 0)
+ {
+ break;
+ }
+
+ nz--;
+ continue;
+ }
+
+ bool bit = decoder.DecodeBit();
+ if (!bit)
+ {
+ continue;
+ }
+
+ if (bu >= 0)
+ {
+ // b[u] += delta;
+ Block8x8F.SetScalarAt(b, u, bu + delta);
+ }
+ else
+ {
+ // b[u] -= delta;
+ Block8x8F.SetScalarAt(b, u, bu - delta);
+ }
+ }
+
+ return zig;
+ }
+
+ private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi)
+ {
+ var b = this.Pointers.Block;
+ //var dc = this.Pointers.Dc;
+ int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].AcTableSelector;
+ if (this.ah != 0)
+ {
+ this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al);
+ }
+ else
+ {
+ int zig = this.zigStart;
+ if (zig == 0)
+ {
+ zig++;
+
+ // Decode the DC coefficient, as specified in section F.2.2.1.
+ byte value =
+ decoder.DecodeHuffman(
+ ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].DcTableSelector]);
+ if (value > 16)
+ {
+ throw new ImageFormatException("Excessive DC component");
+ }
+
+ int deltaDC = decoder.Bits.ReceiveExtend(value, decoder);
+ this.Pointers.Dc[compIndex] += deltaDC;
+
+ // b[0] = dc[compIndex] << al;
+ Block8x8F.SetScalarAt(b, 0, this.Pointers.Dc[compIndex] << al);
+ }
+
+ if (zig <= this.zigEnd && decoder.EobRun > 0)
+ {
+ decoder.EobRun--;
+ }
+ else
+ {
+ // Decode the AC coefficients, as specified in section F.2.2.2.
+ for (; zig <= this.zigEnd; zig++)
+ {
+ byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]);
+ byte val0 = (byte)(value >> 4);
+ byte val1 = (byte)(value & 0x0f);
+ if (val1 != 0)
+ {
+ zig += val0;
+ if (zig > this.zigEnd)
+ {
+ break;
+ }
+
+ int ac = decoder.Bits.ReceiveExtend(val1, decoder);
+
+ // b[Unzig[zig]] = ac << al;
+ Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], ac << this.al);
+ }
+ else
+ {
+ if (val0 != 0x0f)
+ {
+ decoder.EobRun = (ushort)(1 << val0);
+ if (val0 != 0)
+ {
+ decoder.EobRun |= (ushort)decoder.DecodeBits(val0);
+ }
+
+ decoder.EobRun--;
+ break;
+ }
+
+ zig += 0x0f;
+ }
+ }
+ }
+ }
+
+ if (decoder.IsProgressive)
+ {
+ if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0)
+ {
+ // We haven't completely decoded this 8x8 block. Save the coefficients.
+ // this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone();
+ decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * hi) + this.bx] = *b;
+
+ // At this point, we could execute the rest of the loop body to dequantize and
+ // perform the inverse DCT, to save early stages of a progressive image to the
+ // *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
+ // the jpeg.Decode function does not return until the entire image is decoded,
+ // so we "continue" here to avoid wasted computation.
+ return;
+ }
+ }
+
+ // Dequantize, perform the inverse DCT and store the block to the image.
+ Block8x8F.UnZig(b, this.Pointers.QuantiazationTable, this.Pointers.Unzig);
+
+ DCT.TransformIDCT(ref *b, ref *this.Pointers.Temp1, ref *this.Pointers.Temp2);
+
+ var destChannel = decoder.GetDestinationChannel(compIndex);
+ var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by);
+ destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md
new file mode 100644
index 000000000..09e3c80bb
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md
@@ -0,0 +1,25 @@
+## JpegScanDecoder
+Encapsulates the impementation of the Jpeg top-to bottom scan decoder triggered by the `SOS` marker.
+The implementation is optimized to hold most of the necessary data in a single value type, which is intended to be used as an on-stack object.
+
+#### Benefits:
+- Maximized locality of reference by keeping most of the operation data on the stack
+- Reaching this without long parameter lists, most of the values describing the state of the decoder algorithm
+are members of the `JpegScanDecoder` struct
+- Most of the logic related to Scan decoding is refactored & simplified now to live in the methods of `JpegScanDecoder`
+- The first step is done towards separating the stream reading from block processing. They can be refactored later to be executed in two disctinct loops.
+ - The input processing loop can be `async`
+ - The block processing loop can be parallelized
+
+#### Data layout
+
+|JpegScanDecoder |
+|-------------------|
+|Variables |
+|ComponentData |
+|ComponentPointers |
+
+- **ComponentData** holds the "large" data blocks needed for computations (Mostly `Block8x8F`-s)
+- **ComponentPointers** contains pointers to the memory regions of `ComponentData` so they can be easily passed around to pointer based utility methods of `Block8x8F`
+
+
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs
new file mode 100644
index 000000000..a2f439c5a
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs
@@ -0,0 +1,26 @@
+namespace ImageSharp.Formats.Jpg
+{
+ using System.Runtime.InteropServices;
+
+ ///
+ /// Represents a component scan
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct Scan
+ {
+ ///
+ /// Gets or sets the component index.
+ ///
+ public byte Index;
+
+ ///
+ /// Gets or sets the DC table selector
+ ///
+ public byte DcTableSelector;
+
+ ///
+ /// Gets or sets the AC table selector
+ ///
+ public byte AcTableSelector;
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs b/src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs
index cba9c4461..f842a295c 100644
--- a/src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs
+++ b/src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs
@@ -2,16 +2,17 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
-
namespace ImageSharp.Formats.Jpg
{
+ using System;
+
///
- /// Represents an image made up of three color components (luminance, blue chroma, red chroma)
+ /// Represents an image made up of three color components (luminance, blue chroma, red chroma)
///
- internal class YCbCrImage
+ internal class YCbCrImage : IDisposable
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The width.
/// The height.
@@ -20,168 +21,124 @@ namespace ImageSharp.Formats.Jpg
{
int cw, ch;
YCbCrSize(width, height, ratio, out cw, out ch);
- this.YChannel = new byte[width * height];
- this.CbChannel = new byte[cw * ch];
- this.CrChannel = new byte[cw * ch];
+ this.YPixels = CleanPooler.RentCleanArray(width * height);
+ this.CbPixels = CleanPooler.RentCleanArray(cw * ch);
+ this.CrPixels = CleanPooler.RentCleanArray(cw * ch);
this.Ratio = ratio;
+ this.YOffset = 0;
+ this.COffset = 0;
this.YStride = width;
this.CStride = cw;
- this.X = 0;
- this.Y = 0;
- this.Width = width;
- this.Height = height;
- }
-
- ///
- /// Prevents a default instance of the class from being created.
- ///
- private YCbCrImage()
- {
}
///
- /// Provides enumeration of the various available subsample ratios.
+ /// Provides enumeration of the various available subsample ratios.
///
public enum YCbCrSubsampleRatio
{
///
- /// YCbCrSubsampleRatio444
+ /// YCbCrSubsampleRatio444
///
YCbCrSubsampleRatio444,
///
- /// YCbCrSubsampleRatio422
+ /// YCbCrSubsampleRatio422
///
YCbCrSubsampleRatio422,
///
- /// YCbCrSubsampleRatio420
+ /// YCbCrSubsampleRatio420
///
YCbCrSubsampleRatio420,
///
- /// YCbCrSubsampleRatio440
+ /// YCbCrSubsampleRatio440
///
YCbCrSubsampleRatio440,
///
- /// YCbCrSubsampleRatio411
+ /// YCbCrSubsampleRatio411
///
YCbCrSubsampleRatio411,
///
- /// YCbCrSubsampleRatio410
+ /// YCbCrSubsampleRatio410
///
YCbCrSubsampleRatio410,
}
///
- /// Gets or sets the luminance components channel.
- ///
- public byte[] YChannel { get; set; }
-
- ///
- /// Gets or sets the blue chroma components channel.
+ /// Gets an offseted to the Cb channel
///
- public byte[] CbChannel { get; set; }
+ public JpegPixelArea CbChannel => new JpegPixelArea(this.CbPixels, this.CStride, this.COffset);
///
- /// Gets or sets the red chroma components channel.
+ /// Gets the blue chroma components channel.
///
- public byte[] CrChannel { get; set; }
+ public byte[] CbPixels { get; }
///
- /// Gets or sets the Y slice index delta between vertically adjacent pixels.
+ /// Gets the index of the first element of red or blue chroma.
///
- public int YStride { get; set; }
+ public int COffset { get; }
///
- /// Gets or sets the red and blue chroma slice index delta between vertically adjacent pixels
- /// that map to separate chroma samples.
+ /// Gets an offseted to the Cr channel
///
- public int CStride { get; set; }
+ public JpegPixelArea CrChannel => new JpegPixelArea(this.CrPixels, this.CStride, this.COffset);
///
- /// Gets or sets the index of the first luminance element.
+ /// Gets the red chroma components channel.
///
- public int YOffset { get; set; }
+ public byte[] CrPixels { get; }
///
- /// Gets or sets the index of the first element of red or blue chroma.
+ /// Gets the red and blue chroma slice index delta between vertically adjacent pixels
+ /// that map to separate chroma samples.
///
- public int COffset { get; set; }
+ public int CStride { get; }
///
- /// Gets or sets the horizontal position.
+ /// Gets or sets the subsampling ratio.
///
- public int X { get; set; }
+ public YCbCrSubsampleRatio Ratio { get; set; }
///
- /// Gets or sets the vertical position.
+ /// Gets an offseted to the Y channel
///
- public int Y { get; set; }
+ public JpegPixelArea YChannel => new JpegPixelArea(this.YPixels, this.YStride, this.YOffset);
///
- /// Gets or sets the width.
+ /// Gets the index of the first luminance element.
///
- public int Width { get; set; }
+ public int YOffset { get; }
///
- /// Gets or sets the height.
+ /// Gets the luminance components channel.
///
- public int Height { get; set; }
+ public byte[] YPixels { get; }
///
- /// Gets or sets the subsampling ratio.
+ /// Gets the Y slice index delta between vertically adjacent pixels.
///
- public YCbCrSubsampleRatio Ratio { get; set; }
+ public int YStride { get; }
///
- /// Gets an image made up of a subset of the originals pixels.
+ /// Disposes the returning rented arrays to the pools.
///
- /// The x-coordinate of the image.
- /// The y-coordinate of the image.
- /// The width.
- /// The height.
- ///
- /// The .
- ///
- public YCbCrImage Subimage(int x, int y, int width, int height)
+ public void Dispose()
{
- YCbCrImage ret = new YCbCrImage
- {
- Width = width,
- Height = height,
- YChannel = this.YChannel,
- CbChannel = this.CbChannel,
- CrChannel = this.CrChannel,
- Ratio = this.Ratio,
- YStride = this.YStride,
- CStride = this.CStride,
- YOffset = (y * this.YStride) + x,
- COffset = (y * this.CStride) + x
- };
- return ret;
+ CleanPooler.ReturnArray(this.YPixels);
+ CleanPooler.ReturnArray(this.CrPixels);
+ CleanPooler.ReturnArray(this.CbPixels);
}
///
- /// Returns the offset of the first luminance component at the given row
+ /// Returns the offset of the first chroma component at the given row
///
/// The row number.
///
- /// The .
- ///
- public int GetRowYOffset(int y)
- {
- return y * this.YStride;
- }
-
- ///
- /// Returns the offset of the first chroma component at the given row
- ///
- /// The row number.
- ///
- /// The .
+ /// The .
///
public int GetRowCOffset(int y)
{
@@ -203,14 +160,31 @@ namespace ImageSharp.Formats.Jpg
}
///
- /// Returns the height and width of the chroma components
+ /// Returns the offset of the first luminance component at the given row
+ ///
+ /// The row number.
+ ///
+ /// The .
+ ///
+ public int GetRowYOffset(int y)
+ {
+ return y * this.YStride;
+ }
+
+ ///
+ /// Returns the height and width of the chroma components
///
/// The width.
/// The height.
/// The subsampling ratio.
/// The chroma width.
/// The chroma height.
- private static void YCbCrSize(int width, int height, YCbCrSubsampleRatio ratio, out int chromaWidth, out int chromaHeight)
+ private static void YCbCrSize(
+ int width,
+ int height,
+ YCbCrSubsampleRatio ratio,
+ out int chromaWidth,
+ out int chromaHeight)
{
switch (ratio)
{
@@ -243,4 +217,4 @@ namespace ImageSharp.Formats.Jpg
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
index 761ad891e..4b3b26da9 100644
--- a/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
@@ -2,7 +2,6 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
-
namespace ImageSharp.Formats
{
using System;
@@ -10,221 +9,168 @@ namespace ImageSharp.Formats
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
- using ImageSharp.Formats.Jpg;
+ using ImageSharp.Formats.Jpg;
+
///
- /// Performs the jpeg decoding operation.
+ /// Performs the jpeg decoding operation.
///
internal unsafe class JpegDecoderCore : IDisposable
{
///
- /// The maximum (inclusive) number of bits in a Huffman code.
- ///
- internal const int MaxCodeLength = 16;
-
- ///
- /// The maximum (inclusive) number of codes in a Huffman tree.
- ///
- internal const int MaxNCodes = 256;
-
- ///
- /// The log-2 size of the Huffman decoder's look-up table.
- ///
- internal const int LutSize = 8;
-
- ///
- /// The maximum number of color components
- ///
- private const int MaxComponents = 4;
-
- ///
- /// The maximum number of Huffman table classes
- ///
- private const int MaxTc = 1;
-
- ///
- /// The maximum number of Huffman table identifiers
+ /// The maximum number of color components
///
- private const int MaxTh = 3;
-
- private const int ThRowSize = MaxTh + 1;
+ internal const int MaxComponents = 4;
///
- /// The maximum number of quantization tables
+ /// The maximum number of quantization tables
///
private const int MaxTq = 3;
///
- /// The DC table index
+ /// The component array
///
- private const int DcTable = 0;
+ internal Component[] ComponentArray { get; }
///
- /// The AC table index
+ /// The huffman trees
///
- private const int AcTable = 1;
+ internal HuffmanTree[] HuffmanTrees { get; }
///
- /// The component array
+ /// Saved state between progressive-mode scans.
///
- private readonly Component[] componentArray;
+ internal Block8x8F[][] ProgCoeffs { get; }
///
- /// Saved state between progressive-mode scans.
+ /// Quantization tables, in zigzag order.
///
- private readonly Block8x8F[][] progCoeffs;
+ internal Block8x8F[] QuantizationTables { get; }
///
- /// The huffman trees
+ /// A temporary buffer for holding pixels
///
- private readonly HuffmanTree[] huffmanTrees;
+ internal byte[] Temp { get; }
- ///
- /// Quantization tables, in zigzag order.
- ///
- private readonly Block8x8F[] quantizationTables;
+ // TODO: the usage of this buffer is unclean + need to move it to the stack for performance
///
- /// A temporary buffer for holding pixels
+ /// The App14 marker color-space
///
- private readonly byte[] temp;
+ private byte adobeTransform;
///
- /// The byte buffer.
+ /// Whether the image is in CMYK format with an App14 marker
///
- private Bytes bytes;
+ private bool adobeTransformValid;
///
- /// The byte buffer.
+ /// Holds the unprocessed bits that have been taken from the byte-stream.
///
- private Stream inputStream;
+ internal Bits Bits;
- ///
- /// Holds the unprocessed bits that have been taken from the byte-stream.
- ///
- private Bits bits;
+ private JpegPixelArea blackImage;
- ///
- /// The image width
- ///
- private int imageWidth;
+ //private int blockIndex;
///
- /// The image height
+ /// The byte buffer.
///
- private int imageHeight;
+ private Bytes bytes;
///
- /// The number of color components within the image.
+ /// The number of color components within the image.
///
- private int componentCount;
+ internal int ComponentCount { get; private set; }
///
- /// A grayscale image to decode to.
+ /// End-of-Band run, specified in section G.1.2.2.
///
- private GrayImage grayImage;
+ internal ushort EobRun;
///
- /// The full color image to decode to.
+ /// A grayscale image to decode to.
///
- private YCbCrImage ycbcrImage;
+ private JpegPixelArea grayImage;
///
- /// The array of keyline pixels in a CMYK image
+ /// The horizontal resolution. Calculated if the image has a JFIF header.
///
- private byte[] blackPixels;
+ private short horizontalResolution;
///
- /// The width in bytes or a single row of keyline pixels in a CMYK image
+ /// The image height
///
- private int blackStride;
+ internal int ImageHeight { get; private set; }
///
- /// The restart interval
+ /// The image width
///
- private int restartInterval;
+ internal int ImageWidth { get; private set; }
///
- /// Whether the image is interlaced (progressive)
+ /// The byte buffer.
///
- private bool isProgressive;
+ private Stream inputStream;
///
- /// Whether the image has a JFIF header
+ /// Whether the image has a JFIF header
///
private bool isJfif;
///
- /// Whether the image is in CMYK format with an App14 marker
- ///
- private bool adobeTransformValid;
-
- ///
- /// The App14 marker color-space
+ /// Whether the image is interlaced (progressive)
///
- private byte adobeTransform;
+ public bool IsProgressive { get; private set; }
///
- /// End-of-Band run, specified in section G.1.2.2.
+ /// The restart interval
///
- private ushort eobRun;
+ internal int RestartInterval { get; private set; }
///
- /// The horizontal resolution. Calculated if the image has a JFIF header.
+ /// The vertical resolution. Calculated if the image has a JFIF header.
///
- private short horizontalResolution;
+ private short verticalResolution;
///
- /// The vertical resolution. Calculated if the image has a JFIF header.
+ /// The full color image to decode to.
///
- private short verticalResolution;
-
- private int blockIndex;
+ private YCbCrImage ycbcrImage;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
public JpegDecoderCore()
{
- // this.huffmanTrees = new Huffman[MaxTc + 1, MaxTh + 1];
- this.huffmanTrees = new HuffmanTree[(MaxTc + 1) * (MaxTh + 1)];
-
- this.quantizationTables = new Block8x8F[MaxTq + 1];
- this.temp = new byte[2 * Block8x8F.ScalarCount];
- this.componentArray = new Component[MaxComponents];
- this.progCoeffs = new Block8x8F[MaxComponents][];
- this.bits = default(Bits);
+ this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
+ this.QuantizationTables = new Block8x8F[MaxTq + 1];
+ this.Temp = new byte[2 * Block8x8F.ScalarCount];
+ this.ComponentArray = new Component[MaxComponents];
+ this.ProgCoeffs = new Block8x8F[MaxComponents][];
+ this.Bits = default(Bits);
this.bytes = Bytes.Create();
-
- // TODO: This looks like it could be static.
- for (int i = 0; i < MaxTc + 1; i++)
- {
- for (int j = 0; j < MaxTh + 1; j++)
- {
- this.huffmanTrees[(i * ThRowSize) + j].Init(LutSize, MaxNCodes, MaxCodeLength);
- }
- }
}
-
+
///
- /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
- /// It's better tho have an error code for this!
+ /// ReadByteStuffedByte was throwing exceptions on normal execution path (very inefficent)
+ /// It's better tho have an error code for this!
///
internal enum ErrorCodes
{
///
- /// NoError
+ /// NoError
///
NoError,
///
- /// MissingFF00
+ /// MissingFF00
///
MissingFF00
}
///
- /// Gets or sets the byte buffer.
+ /// Gets or sets the byte buffer.
///
public Bytes Bytes
{
@@ -240,7 +186,7 @@ namespace ImageSharp.Formats
}
///
- /// Gets the input stream.
+ /// Gets the input stream.
///
public Stream InputStream
{
@@ -251,8 +197,8 @@ namespace ImageSharp.Formats
}
///
- /// Decodes the image from the specified this._stream and sets
- /// the data to image.
+ /// Decodes the image from the specified this._stream and sets
+ /// the data to image.
///
/// The pixel format.
/// The image, where the data should be set to.
@@ -264,8 +210,8 @@ namespace ImageSharp.Formats
this.inputStream = stream;
// Check for the Start Of Image marker.
- this.ReadFull(this.temp, 0, 2);
- if (this.temp[0] != JpegConstants.Markers.XFF || this.temp[1] != JpegConstants.Markers.SOI)
+ this.ReadFull(this.Temp, 0, 2);
+ if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI)
{
throw new ImageFormatException("Missing SOI marker.");
}
@@ -273,8 +219,8 @@ namespace ImageSharp.Formats
// Process the remaining segments until the End Of Image marker.
while (true)
{
- this.ReadFull(this.temp, 0, 2);
- while (this.temp[0] != 0xff)
+ this.ReadFull(this.Temp, 0, 2);
+ while (this.Temp[0] != 0xff)
{
// Strictly speaking, this is a format error. However, libjpeg is
// liberal in what it accepts. As of version 9, next_marker in
@@ -293,11 +239,11 @@ namespace ImageSharp.Formats
// mechanism within a scan (the RST[0-7] markers).
// Note that extraneous 0xff bytes in e.g. SOS data are escaped as
// "\xff\x00", and so are detected a little further down below.
- this.temp[0] = this.temp[1];
- this.temp[1] = this.ReadByte();
+ this.Temp[0] = this.Temp[1];
+ this.Temp[1] = this.ReadByte();
}
- byte marker = this.temp[1];
+ byte marker = this.Temp[1];
if (marker == 0)
{
// Treat "\xff\x00" as extraneous data.
@@ -330,8 +276,8 @@ namespace ImageSharp.Formats
// Read the 16-bit length of the segment. The value includes the 2 bytes for the
// length itself, so we subtract 2 to get the number of remaining bytes.
- this.ReadFull(this.temp, 0, 2);
- int remaining = (this.temp[0] << 8) + this.temp[1] - 2;
+ this.ReadFull(this.Temp, 0, 2);
+ int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2;
if (remaining < 0)
{
throw new ImageFormatException("Short segment length.");
@@ -342,7 +288,7 @@ namespace ImageSharp.Formats
case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2:
- this.isProgressive = marker == JpegConstants.Markers.SOF2;
+ this.IsProgressive = marker == JpegConstants.Markers.SOF2;
this.ProcessStartOfFrameMarker(remaining);
if (configOnly && this.isJfif)
{
@@ -420,17 +366,18 @@ namespace ImageSharp.Formats
}
}
- if (this.grayImage != null)
+ if (this.grayImage.IsInitialized)
{
- this.ConvertFromGrayScale(this.imageWidth, this.imageHeight, image);
+ this.ConvertFromGrayScale(this.ImageWidth, this.ImageHeight, image);
}
else if (this.ycbcrImage != null)
{
- if (this.componentCount == 4)
+ if (this.ComponentCount == 4)
{
if (!this.adobeTransformValid)
{
- throw new ImageFormatException("Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata");
+ throw new ImageFormatException(
+ "Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata");
}
// See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
@@ -438,26 +385,26 @@ namespace ImageSharp.Formats
// TODO: YCbCrA?
if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck)
{
- this.ConvertFromYcck(this.imageWidth, this.imageHeight, image);
+ this.ConvertFromYcck(this.ImageWidth, this.ImageHeight, image);
}
else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
// Assume CMYK
- this.ConvertFromCmyk(this.imageWidth, this.imageHeight, image);
+ this.ConvertFromCmyk(this.ImageWidth, this.ImageHeight, image);
}
return;
}
- if (this.componentCount == 3)
+ if (this.ComponentCount == 3)
{
if (this.IsRGB())
{
- this.ConvertFromRGB(this.imageWidth, this.imageHeight, image);
+ this.ConvertFromRGB(this.ImageWidth, this.ImageHeight, image);
return;
}
- this.ConvertFromYCbCr(this.imageWidth, this.imageHeight, image);
+ this.ConvertFromYCbCr(this.ImageWidth, this.ImageHeight, image);
return;
}
@@ -470,22 +417,25 @@ namespace ImageSharp.Formats
}
///
- /// Dispose
+ /// Dispose
///
public void Dispose()
{
- for (int i = 0; i < this.huffmanTrees.Length; i++)
+ for (int i = 0; i < this.HuffmanTrees.Length; i++)
{
- this.huffmanTrees[i].Dispose();
+ this.HuffmanTrees[i].Dispose();
}
+ this.ycbcrImage?.Dispose();
this.bytes.Dispose();
+ this.grayImage.ReturnPooled();
+ this.blackImage.ReturnPooled();
}
-
+
///
- /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
+ /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
///
- /// The
+ /// The
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte()
{
@@ -493,8 +443,47 @@ namespace ImageSharp.Formats
}
///
- /// Optimized method to pack bytes to the image from the YCbCr color space.
- /// This is faster than implicit casting as it avoids double packing.
+ /// Reads exactly length bytes into data. It does not care about byte stuffing.
+ ///
+ /// The data to write to.
+ /// The offset in the source buffer
+ /// The number of bytes to read
+ internal void ReadFull(byte[] data, int offset, int length)
+ {
+ // Unread the overshot bytes, if any.
+ if (this.bytes.UnreadableBytes != 0)
+ {
+ if (this.Bits.UnreadBits >= 8)
+ {
+ this.UnreadByteStuffedByte();
+ }
+
+ this.bytes.UnreadableBytes = 0;
+ }
+
+ while (length > 0)
+ {
+ if (this.bytes.J - this.bytes.I >= length)
+ {
+ Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, length);
+ this.bytes.I += length;
+ length -= length;
+ }
+ else
+ {
+ Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, this.bytes.J - this.bytes.I);
+ offset += this.bytes.J - this.bytes.I;
+ length -= this.bytes.J - this.bytes.I;
+ this.bytes.I += this.bytes.J - this.bytes.I;
+
+ this.bytes.Fill(this.inputStream);
+ }
+ }
+ }
+
+ ///
+ /// Optimized method to pack bytes to the image from the YCbCr color space.
+ /// This is faster than implicit casting as it avoids double packing.
///
/// The pixel format.
/// The packed pixel.
@@ -516,526 +505,636 @@ namespace ImageSharp.Formats
}
///
- /// Processes a Define Huffman Table marker, and initializes a huffman
- /// struct from its contents. Specified in section B.2.4.2.
+ /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header.
///
- /// The remaining bytes in the segment block.
- private void ProcessDefineHuffmanTablesMarker(int remaining)
+ /// The pixel format.
+ /// The image to assign the resolution to.
+ private void AssignResolution(Image image)
+ where TColor : struct, IPackedPixel, IEquatable
{
- while (remaining > 0)
+ if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0)
{
- if (remaining < 17)
- {
- throw new ImageFormatException("DHT has wrong length");
- }
-
- this.ReadFull(this.temp, 0, 17);
-
- int tc = this.temp[0] >> 4;
- if (tc > MaxTc)
- {
- throw new ImageFormatException("Bad Tc value");
- }
-
- int th = this.temp[0] & 0x0f;
- if (th > MaxTh || (!this.isProgressive && (th > 1)))
- {
- throw new ImageFormatException("Bad Th value");
- }
-
- this.ProcessDefineHuffmanTablesMarkerLoop(ref this.huffmanTrees[(tc * ThRowSize) + th], ref remaining);
+ image.HorizontalResolution = this.horizontalResolution;
+ image.VerticalResolution = this.verticalResolution;
}
}
- private void ProcessDefineHuffmanTablesMarkerLoop(ref HuffmanTree huffmanTree, ref int remaining)
+ ///
+ /// Converts the image from the original CMYK image pixels.
+ ///
+ /// The pixel format.
+ /// The image width.
+ /// The image height.
+ /// The image.
+ private void ConvertFromCmyk(int width, int height, Image image)
+ where TColor : struct, IPackedPixel, IEquatable
{
- // Read nCodes and huffman.Valuess (and derive h.Length).
- // nCodes[i] is the number of codes with code length i.
- // h.Length is the total number of codes.
- huffmanTree.Length = 0;
+ int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
- int[] ncodes = new int[MaxCodeLength];
- for (int i = 0; i < ncodes.Length; i++)
- {
- ncodes[i] = this.temp[i + 1];
- huffmanTree.Length += ncodes[i];
- }
+ image.InitPixels(width, height);
- if (huffmanTree.Length == 0)
+ using (PixelAccessor pixels = image.Lock())
{
- throw new ImageFormatException("Huffman table has zero length");
- }
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
- if (huffmanTree.Length > MaxNCodes)
- {
- throw new ImageFormatException("Huffman table has excessive length");
- }
+ for (int x = 0; x < width; x++)
+ {
+ byte cyan = this.ycbcrImage.YPixels[yo + x];
+ byte magenta = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte yellow = this.ycbcrImage.CrPixels[co + (x / scale)];
- remaining -= huffmanTree.Length + 17;
- if (remaining < 0)
- {
- throw new ImageFormatException("DHT has wrong length");
+ TColor packed = default(TColor);
+ this.PackCmyk(ref packed, cyan, magenta, yellow, x, y);
+ pixels[x, y] = packed;
+ }
+ });
}
- this.ReadFull(huffmanTree.Values, 0, huffmanTree.Length);
-
- // Derive the look-up table.
- for (int i = 0; i < huffmanTree.Lut.Length; i++)
- {
- huffmanTree.Lut[i] = 0;
- }
+ this.AssignResolution(image);
+ }
- uint x = 0, code = 0;
+ ///
+ /// Converts the image from the original grayscale image pixels.
+ ///
+ /// The pixel format.
+ /// The image width.
+ /// The image height.
+ /// The image.
+ private void ConvertFromGrayScale(int width, int height, Image image)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ image.InitPixels(width, height);
- for (int i = 0; i < LutSize; i++)
+ using (PixelAccessor pixels = image.Lock())
{
- code <<= 1;
-
- for (int j = 0; j < ncodes[i]; j++)
- {
- // The codeLength is 1+i, so shift code by 8-(1+i) to
- // calculate the high bits for every 8-bit sequence
- // whose codeLength's high bits matches code.
- // The high 8 bits of lutValue are the encoded value.
- // The low 8 bits are 1 plus the codeLength.
- byte base2 = (byte)(code << (7 - i));
- ushort lutValue = (ushort)((huffmanTree.Values[x] << 8) | (2 + i));
-
- for (int k = 0; k < 1 << (7 - i); k++)
- {
- huffmanTree.Lut[base2 | k] = lutValue;
- }
+ Parallel.For(
+ 0,
+ height,
+ Bootstrapper.ParallelOptions,
+ y =>
+ {
+ int yoff = this.grayImage.GetRowOffset(y);
+ for (int x = 0; x < width; x++)
+ {
+ byte rgb = this.grayImage.Pixels[yoff + x];
- code++;
- x++;
- }
+ TColor packed = default(TColor);
+ packed.PackFromBytes(rgb, rgb, rgb, 255);
+ pixels[x, y] = packed;
+ }
+ });
}
- // Derive minCodes, maxCodes, and indices.
- int c = 0, index = 0;
- for (int i = 0; i < ncodes.Length; i++)
- {
- int nc = ncodes[i];
- if (nc == 0)
- {
- huffmanTree.MinCodes[i] = -1;
- huffmanTree.MaxCodes[i] = -1;
- huffmanTree.Indices[i] = -1;
- }
- else
- {
- huffmanTree.MinCodes[i] = c;
- huffmanTree.MaxCodes[i] = c + nc - 1;
- huffmanTree.Indices[i] = index;
- c += nc;
- index += nc;
- }
-
- c <<= 1;
- }
+ this.AssignResolution(image);
}
///
- /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value.
+ /// Converts the image from the original RBG image pixels.
///
- /// The huffman value
- /// The
- private byte DecodeHuffman(ref HuffmanTree huffmanTree)
+ /// The pixel format.
+ /// The image width.
+ /// The height.
+ /// The image.
+ private void ConvertFromRGB(int width, int height, Image image)
+ where TColor : struct, IPackedPixel, IEquatable
{
- // Copy stuff to the stack:
- if (huffmanTree.Length == 0)
- {
- throw new ImageFormatException("Uninitialized Huffman table");
- }
-
- if (this.bits.UnreadBits < 8)
- {
- ErrorCodes errorCode = this.bits.EnsureNBits(8, this);
-
- if (errorCode == ErrorCodes.NoError)
- {
- ushort v = huffmanTree.Lut[(this.bits.Accumulator >> (this.bits.UnreadBits - LutSize)) & 0xff];
-
- if (v != 0)
- {
- byte n = (byte)((v & 0xff) - 1);
- this.bits.UnreadBits -= n;
- this.bits.Mask >>= n;
- return (byte)(v >> 8);
- }
- }
- else
- {
- this.UnreadByteStuffedByte();
- }
- }
+ int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
+ image.InitPixels(width, height);
- int code = 0;
- for (int i = 0; i < MaxCodeLength; i++)
+ using (PixelAccessor pixels = image.Lock())
{
- if (this.bits.UnreadBits == 0)
- {
- ErrorCodes errorCode = this.bits.EnsureNBits(1, this);
- if (errorCode != ErrorCodes.NoError)
- {
- throw new MissingFF00Exception();
- }
- }
-
- if ((this.bits.Accumulator & this.bits.Mask) != 0)
- {
- code |= 1;
- }
-
- this.bits.UnreadBits--;
- this.bits.Mask >>= 1;
+ Parallel.For(
+ 0,
+ height,
+ Bootstrapper.ParallelOptions,
+ y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
- if (code <= huffmanTree.MaxCodes[i])
- {
- return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]];
- }
+ for (int x = 0; x < width; x++)
+ {
+ byte red = this.ycbcrImage.YPixels[yo + x];
+ byte green = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte blue = this.ycbcrImage.CrPixels[co + (x / scale)];
- code <<= 1;
+ TColor packed = default(TColor);
+ packed.PackFromBytes(red, green, blue, 255);
+ pixels[x, y] = packed;
+ }
+ });
}
- throw new ImageFormatException("Bad Huffman code");
+ this.AssignResolution(image);
}
///
- /// Decodes a single bit
+ /// Converts the image from the original YCbCr image pixels.
///
- /// The
- private bool DecodeBit()
- {
- if (this.bits.UnreadBits == 0)
- {
- ErrorCodes errorCode = this.bits.EnsureNBits(1, this);
- if (errorCode != ErrorCodes.NoError)
- {
- throw new MissingFF00Exception();
- }
+ /// The pixel format.
+ /// The image width.
+ /// The image height.
+ /// The image.
+ private void ConvertFromYCbCr(int width, int height, Image image)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
+ image.InitPixels(width, height);
+
+ using (PixelAccessor pixels = image.Lock())
+ {
+ Parallel.For(
+ 0,
+ height,
+ Bootstrapper.ParallelOptions,
+ y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
+
+ for (int x = 0; x < width; x++)
+ {
+ byte yy = this.ycbcrImage.YPixels[yo + x];
+ byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
+
+ TColor packed = default(TColor);
+ PackYcbCr(ref packed, yy, cb, cr);
+ pixels[x, y] = packed;
+ }
+ });
}
- bool ret = (this.bits.Accumulator & this.bits.Mask) != 0;
- this.bits.UnreadBits--;
- this.bits.Mask >>= 1;
- return ret;
+ this.AssignResolution(image);
}
///
- /// Decodes the given number of bits
+ /// Converts the image from the original YCCK image pixels.
///
- /// The number of bits to decode.
- /// The
- private uint DecodeBits(int count)
+ /// The pixel format.
+ /// The image width.
+ /// The image height.
+ /// The image.
+ private void ConvertFromYcck(int width, int height, Image image)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor;
+
+ image.InitPixels(width, height);
+
+ using (PixelAccessor pixels = image.Lock())
+ {
+ Parallel.For(
+ 0,
+ height,
+ y =>
+ {
+ int yo = this.ycbcrImage.GetRowYOffset(y);
+ int co = this.ycbcrImage.GetRowCOffset(y);
+
+ for (int x = 0; x < width; x++)
+ {
+ byte yy = this.ycbcrImage.YPixels[yo + x];
+ byte cb = this.ycbcrImage.CbPixels[co + (x / scale)];
+ byte cr = this.ycbcrImage.CrPixels[co + (x / scale)];
+
+ TColor packed = default(TColor);
+ this.PackYcck(ref packed, yy, cb, cr, x, y);
+ pixels[x, y] = packed;
+ }
+ });
+ }
+
+ this.AssignResolution(image);
+ }
+
+ ///
+ /// Decodes a single bit
+ ///
+ /// The
+ internal bool DecodeBit()
{
- if (this.bits.UnreadBits < count)
+ if (this.Bits.UnreadBits == 0)
{
- ErrorCodes errorCode = this.bits.EnsureNBits(count, this);
+ ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
if (errorCode != ErrorCodes.NoError)
{
throw new MissingFF00Exception();
}
}
- uint ret = this.bits.Accumulator >> (this.bits.UnreadBits - count);
- ret = (uint)(ret & ((1 << count) - 1));
- this.bits.UnreadBits -= count;
- this.bits.Mask >>= count;
+ bool ret = (this.Bits.Accumulator & this.Bits.Mask) != 0;
+ this.Bits.UnreadBits--;
+ this.Bits.Mask >>= 1;
return ret;
}
///
- /// Undoes the most recent ReadByteStuffedByte call,
- /// giving a byte of data back from bits to bytes. The Huffman look-up table
- /// requires at least 8 bits for look-up, which means that Huffman decoding can
- /// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
- /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
+ /// Decodes the given number of bits
///
- private void UnreadByteStuffedByte()
+ /// The number of bits to decode.
+ /// The
+ internal uint DecodeBits(int count)
{
- this.bytes.I -= this.bytes.UnreadableBytes;
- this.bytes.UnreadableBytes = 0;
- if (this.bits.UnreadBits >= 8)
+ if (this.Bits.UnreadBits < count)
{
- this.bits.Accumulator >>= 8;
- this.bits.UnreadBits -= 8;
- this.bits.Mask >>= 8;
+ ErrorCodes errorCode = this.Bits.EnsureNBits(count, this);
+ if (errorCode != ErrorCodes.NoError)
+ {
+ throw new MissingFF00Exception();
+ }
}
+
+ uint ret = this.Bits.Accumulator >> (this.Bits.UnreadBits - count);
+ ret = (uint)(ret & ((1 << count) - 1));
+ this.Bits.UnreadBits -= count;
+ this.Bits.Mask >>= count;
+ return ret;
}
///
- /// Reads exactly length bytes into data. It does not care about byte stuffing.
+ /// Returns the next Huffman-coded value from the bit-stream, decoded according to the given value.
///
- /// The data to write to.
- /// The offset in the source buffer
- /// The number of bytes to read
- private void ReadFull(byte[] data, int offset, int length)
+ /// The huffman value
+ /// The
+ internal byte DecodeHuffman(ref HuffmanTree huffmanTree)
{
- // Unread the overshot bytes, if any.
- if (this.bytes.UnreadableBytes != 0)
+ // Copy stuff to the stack:
+ if (huffmanTree.Length == 0)
{
- if (this.bits.UnreadBits >= 8)
- {
- this.UnreadByteStuffedByte();
- }
-
- this.bytes.UnreadableBytes = 0;
+ throw new ImageFormatException("Uninitialized Huffman table");
}
- while (length > 0)
+ if (this.Bits.UnreadBits < 8)
{
- if (this.bytes.J - this.bytes.I >= length)
+ ErrorCodes errorCode = this.Bits.EnsureNBits(8, this);
+
+ if (errorCode == ErrorCodes.NoError)
{
- Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, length);
- this.bytes.I += length;
- length -= length;
+ ushort v =
+ huffmanTree.Lut[(this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSize)) & 0xff];
+
+ if (v != 0)
+ {
+ byte n = (byte)((v & 0xff) - 1);
+ this.Bits.UnreadBits -= n;
+ this.Bits.Mask >>= n;
+ return (byte)(v >> 8);
+ }
}
else
{
- Array.Copy(this.bytes.Buffer, this.bytes.I, data, offset, this.bytes.J - this.bytes.I);
- offset += this.bytes.J - this.bytes.I;
- length -= this.bytes.J - this.bytes.I;
- this.bytes.I += this.bytes.J - this.bytes.I;
-
- this.bytes.Fill(this.inputStream);
+ this.UnreadByteStuffedByte();
}
}
- }
- ///
- /// Skips the next n bytes.
- ///
- /// The number of bytes to ignore.
- private void Skip(int count)
- {
- // Unread the overshot bytes, if any.
- if (this.bytes.UnreadableBytes != 0)
+ int code = 0;
+ for (int i = 0; i < HuffmanTree.MaxCodeLength; i++)
{
- if (this.bits.UnreadBits >= 8)
+ if (this.Bits.UnreadBits == 0)
{
- this.UnreadByteStuffedByte();
+ ErrorCodes errorCode = this.Bits.EnsureNBits(1, this);
+ if (errorCode != ErrorCodes.NoError)
+ {
+ throw new MissingFF00Exception();
+ }
}
- this.bytes.UnreadableBytes = 0;
- }
-
- while (true)
- {
- int m = this.bytes.J - this.bytes.I;
- if (m > count)
+ if ((this.Bits.Accumulator & this.Bits.Mask) != 0)
{
- m = count;
+ code |= 1;
}
- this.bytes.I += m;
- count -= m;
- if (count == 0)
+ this.Bits.UnreadBits--;
+ this.Bits.Mask >>= 1;
+
+ if (code <= huffmanTree.MaxCodes[i])
{
- break;
+ return huffmanTree.Values[huffmanTree.Indices[i] + code - huffmanTree.MinCodes[i]];
}
- this.bytes.Fill(this.inputStream);
+ code <<= 1;
+ }
+
+ throw new ImageFormatException("Bad Huffman code");
+ }
+
+ internal JpegPixelArea GetDestinationChannel(int compIndex)
+ {
+ if (this.ComponentCount == 1)
+ {
+ return this.grayImage;
+ }
+ else
+ {
+ switch (compIndex)
+ {
+ case 0:
+ return this.ycbcrImage.YChannel;
+ case 1:
+ return this.ycbcrImage.CbChannel;
+ case 2:
+ return this.ycbcrImage.CrChannel;
+ case 3:
+ return this.blackImage;
+ default:
+ throw new ImageFormatException("Too many components");
+ }
}
}
///
- /// Processes the Start of Frame marker. Specified in section B.2.2.
+ /// Returns a value indicating whether the image in an RGB image.
///
- /// The remaining bytes in the segment block.
- private void ProcessStartOfFrameMarker(int remaining)
+ ///
+ /// The .
+ ///
+ private bool IsRGB()
{
- if (this.componentCount != 0)
+ if (this.isJfif)
{
- throw new ImageFormatException("Multiple SOF markers");
+ return false;
}
- switch (remaining)
+ if (this.adobeTransformValid && this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown)
{
- case 6 + (3 * 1): // Grayscale image.
- this.componentCount = 1;
- break;
- case 6 + (3 * 3): // YCbCr or RGB image.
- this.componentCount = 3;
- break;
- case 6 + (3 * 4): // YCbCrK or CMYK image.
- this.componentCount = 4;
- break;
- default:
- throw new ImageFormatException("Incorrect number of components");
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
+ // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr.
+ return true;
}
- this.ReadFull(this.temp, 0, remaining);
+ return this.ComponentArray[0].Identifier == 'R' && this.ComponentArray[1].Identifier == 'G'
+ && this.ComponentArray[2].Identifier == 'B';
+ }
- // We only support 8-bit precision.
- if (this.temp[0] != 8)
+ ///
+ /// Makes the image from the buffer.
+ ///
+ /// The horizontal MCU count
+ /// The vertical MCU count
+ private void MakeImage(int mxx, int myy)
+ {
+ if (this.grayImage.IsInitialized || this.ycbcrImage != null)
{
- throw new ImageFormatException("Only 8-Bit precision supported.");
+ return;
}
- this.imageHeight = (this.temp[1] << 8) + this.temp[2];
- this.imageWidth = (this.temp[3] << 8) + this.temp[4];
- if (this.temp[5] != this.componentCount)
+ if (this.ComponentCount == 1)
{
- throw new ImageFormatException("SOF has wrong length");
+ this.grayImage = JpegPixelArea.CreatePooled(8 * mxx, 8 * myy);
}
-
- for (int i = 0; i < this.componentCount; i++)
+ else
{
- this.componentArray[i].Identifier = this.temp[6 + (3 * i)];
+ int h0 = this.ComponentArray[0].HorizontalFactor;
+ int v0 = this.ComponentArray[0].VerticalFactor;
+ int horizontalRatio = h0 / this.ComponentArray[1].HorizontalFactor;
+ int verticalRatio = v0 / this.ComponentArray[1].VerticalFactor;
- // Section B.2.2 states that "the value of C_i shall be different from
- // the values of C_1 through C_(i-1)".
- for (int j = 0; j < i; j++)
+ YCbCrImage.YCbCrSubsampleRatio ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444;
+ switch ((horizontalRatio << 4) | verticalRatio)
{
- if (this.componentArray[i].Identifier == this.componentArray[j].Identifier)
- {
- throw new ImageFormatException("Repeated component identifier");
- }
+ case 0x11:
+ ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444;
+ break;
+ case 0x12:
+ ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440;
+ break;
+ case 0x21:
+ ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422;
+ break;
+ case 0x22:
+ ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420;
+ break;
+ case 0x41:
+ ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411;
+ break;
+ case 0x42:
+ ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410;
+ break;
}
- this.componentArray[i].Selector = this.temp[8 + (3 * i)];
- if (this.componentArray[i].Selector > MaxTq)
- {
- throw new ImageFormatException("Bad Tq value");
- }
+ this.ycbcrImage = new YCbCrImage(8 * h0 * mxx, 8 * v0 * myy, ratio);
- byte hv = this.temp[7 + (3 * i)];
- int h = hv >> 4;
- int v = hv & 0x0f;
- if (h < 1 || h > 4 || v < 1 || v > 4)
+ if (this.ComponentCount == 4)
{
- throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio");
- }
+ int h3 = this.ComponentArray[3].HorizontalFactor;
+ int v3 = this.ComponentArray[3].VerticalFactor;
- if (h == 3 || v == 3)
- {
- throw new ImageFormatException("Lnsupported subsampling ratio");
+ this.blackImage = JpegPixelArea.CreatePooled(8 * h3 * mxx, 8 * v3 * myy);
}
+ }
+ }
- switch (this.componentCount)
- {
- case 1:
+ ///
+ /// Optimized method to pack bytes to the image from the CMYK color space.
+ /// This is faster than implicit casting as it avoids double packing.
+ ///
+ /// The pixel format.
+ /// The packed pixel.
+ /// The cyan component.
+ /// The magenta component.
+ /// The yellow component.
+ /// The x-position within the image.
+ /// The y-position within the image.
+ private void PackCmyk(ref TColor packed, byte c, byte m, byte y, int xx, int yy)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ // Get keyline
+ float keyline = (255 - this.blackImage[xx, yy]) / 255F;
- // If a JPEG image has only one component, section A.2 says "this data
- // is non-interleaved by definition" and section A.2.2 says "[in this
- // case...] the order of data units within a scan shall be left-to-right
- // and top-to-bottom... regardless of the values of H_1 and V_1". Section
- // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be
- // one data unit". Similarly, section A.1.1 explains that it is the ratio
- // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale
- // images, H_1 is the maximum H_j for all components j, so that ratio is
- // always 1. The component's (h, v) is effectively always (1, 1): even if
- // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8
- // MCUs, not two 16x8 MCUs.
- h = 1;
- v = 1;
- break;
+ // Convert back to RGB. CMY are not inverted
+ byte r = (byte)(((c / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
+ byte g = (byte)(((m / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
+ byte b = (byte)(((y / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
- case 3:
+ packed.PackFromBytes(r, g, b, 255);
+ }
- // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0,
- // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the
- // (h, v) values for the Y component are either (1, 1), (1, 2),
- // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values
- // must be a multiple of the Cb and Cr component's values. We also
- // assume that the two chroma components have the same subsampling
- // ratio.
- switch (i)
- {
- case 0:
- {
- // Y.
- // We have already verified, above, that h and v are both
- // either 1, 2 or 4, so invalid (h, v) combinations are those
- // with v == 4.
- if (v == 4)
- {
- throw new ImageFormatException("Unsupported subsampling ratio");
- }
+ ///
+ /// Optimized method to pack bytes to the image from the YCCK color space.
+ /// This is faster than implicit casting as it avoids double packing.
+ ///
+ /// The pixel format.
+ /// The packed pixel.
+ /// The y luminance component.
+ /// The cb chroma component.
+ /// The cr chroma component.
+ /// The x-position within the image.
+ /// The y-position within the image.
+ private void PackYcck(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get
+ // CMY, and patch in the original K. The RGB to CMY inversion cancels
+ // out the 'Adobe inversion' described in the applyBlack doc comment
+ // above, so in practice, only the fourth channel (black) is inverted.
+ // TODO: We can speed this up further with Vector4
+ int ccb = cb - 128;
+ int ccr = cr - 128;
- break;
- }
+ // First convert from YCbCr to CMY
+ float cyan = (y + (1.402F * ccr)).Clamp(0, 255) / 255F;
+ float magenta = (y - (0.34414F * ccb) - (0.71414F * ccr)).Clamp(0, 255) / 255F;
+ float yellow = (y + (1.772F * ccb)).Clamp(0, 255) / 255F;
- case 1:
- {
- // Cb.
- if (this.componentArray[0].HorizontalFactor % h != 0
- || this.componentArray[0].VerticalFactor % v != 0)
- {
- throw new ImageFormatException("Unsupported subsampling ratio");
- }
+ // Get keyline
+ float keyline = (255 - this.blackImage[xx, yy]) / 255F;
- break;
- }
+ // Convert back to RGB
+ byte r = (byte)(((1 - cyan) * (1 - keyline)).Clamp(0, 1) * 255);
+ byte g = (byte)(((1 - magenta) * (1 - keyline)).Clamp(0, 1) * 255);
+ byte b = (byte)(((1 - yellow) * (1 - keyline)).Clamp(0, 1) * 255);
- case 2:
- {
- // Cr.
- if (this.componentArray[1].HorizontalFactor != h
- || this.componentArray[1].VerticalFactor != v)
- {
- throw new ImageFormatException("Unsupported subsampling ratio");
- }
+ packed.PackFromBytes(r, g, b, 255);
+ }
- break;
- }
- }
+ ///
+ /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters.
+ /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not
+ /// deleted by default when deleting all metadata because it may affect the appearance of the image.
+ ///
+ /// The remaining number of bytes in the stream.
+ private void ProcessApp14Marker(int remaining)
+ {
+ if (remaining < 12)
+ {
+ this.Skip(remaining);
+ return;
+ }
- break;
+ this.ReadFull(this.Temp, 0, 12);
+ remaining -= 12;
- case 4:
+ if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b'
+ && this.Temp[4] == 'e')
+ {
+ this.adobeTransformValid = true;
+ this.adobeTransform = this.Temp[11];
+ }
- // For 4-component images (either CMYK or YCbCrK), we only support two
- // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
- // Theoretically, 4-component JPEG images could mix and match hv values
- // but in practice, those two combinations are the only ones in use,
- // and it simplifies the applyBlack code below if we can assume that:
- // - for CMYK, the C and K channels have full samples, and if the M
- // and Y channels subsample, they subsample both horizontally and
- // vertically.
- // - for YCbCrK, the Y and K channels have full samples.
- switch (i)
- {
- case 0:
- if (hv != 0x11 && hv != 0x22)
- {
- throw new ImageFormatException("Unsupported subsampling ratio");
- }
+ if (remaining > 0)
+ {
+ this.Skip(remaining);
+ }
+ }
- break;
- case 1:
- case 2:
- if (hv != 0x11)
- {
- throw new ImageFormatException("Unsupported subsampling ratio");
- }
+ ///
+ /// Processes the App1 marker retrieving any stored metadata
+ ///
+ /// The pixel format.
+ /// The remaining bytes in the segment block.
+ /// The image.
+ private void ProcessApp1Marker(int remaining, Image image)
+ where TColor : struct, IPackedPixel, IEquatable
+ {
+ if (remaining < 6)
+ {
+ this.Skip(remaining);
+ return;
+ }
- break;
- case 3:
- if (this.componentArray[0].HorizontalFactor != h
- || this.componentArray[0].VerticalFactor != v)
- {
- throw new ImageFormatException("Unsupported subsampling ratio");
- }
+ byte[] profile = new byte[remaining];
+ this.ReadFull(profile, 0, remaining);
- break;
- }
+ if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0'
+ && profile[5] == '\0')
+ {
+ image.ExifProfile = new ExifProfile(profile);
+ }
+ }
- break;
+ ///
+ /// Processes the application header containing the JFIF identifier plus extra data.
+ ///
+ /// The remaining bytes in the segment block.
+ private void ProcessApplicationHeader(int remaining)
+ {
+ if (remaining < 5)
+ {
+ this.Skip(remaining);
+ return;
+ }
+
+ this.ReadFull(this.Temp, 0, 13);
+ remaining -= 13;
+
+ // TODO: We should be using constants for this.
+ this.isJfif = this.Temp[0] == 'J' && this.Temp[1] == 'F' && this.Temp[2] == 'I' && this.Temp[3] == 'F'
+ && this.Temp[4] == '\x00';
+
+ if (this.isJfif)
+ {
+ this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[10] << 8));
+ this.verticalResolution = (short)(this.Temp[11] + (this.Temp[12] << 8));
+ }
+
+ if (remaining > 0)
+ {
+ this.Skip(remaining);
+ }
+ }
+
+ ///
+ /// Processes a Define Huffman Table marker, and initializes a huffman
+ /// struct from its contents. Specified in section B.2.4.2.
+ ///
+ /// The remaining bytes in the segment block.
+ private void ProcessDefineHuffmanTablesMarker(int remaining)
+ {
+ while (remaining > 0)
+ {
+ if (remaining < 17)
+ {
+ throw new ImageFormatException("DHT has wrong length");
+ }
+
+ this.ReadFull(this.Temp, 0, 17);
+
+ int tc = this.Temp[0] >> 4;
+ if (tc > HuffmanTree.MaxTc)
+ {
+ throw new ImageFormatException("Bad Tc value");
+ }
+
+ int th = this.Temp[0] & 0x0f;
+ if (th > HuffmanTree.MaxTh || (!this.IsProgressive && (th > 1)))
+ {
+ throw new ImageFormatException("Bad Th value");
}
- this.componentArray[i].HorizontalFactor = h;
- this.componentArray[i].VerticalFactor = v;
+ int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th;
+ this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop(this, this.Temp, ref remaining);
+ }
+ }
+
+ ///
+ /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in
+ /// macroblocks
+ ///
+ /// The remaining bytes in the segment block.
+ private void ProcessDefineRestartIntervalMarker(int remaining)
+ {
+ if (remaining != 2)
+ {
+ throw new ImageFormatException("DRI has wrong length");
}
+
+ this.ReadFull(this.Temp, 0, 2);
+ this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1];
}
///
- /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1.
+ /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1.
///
/// The remaining bytes in the segment block.
///
- /// Thrown if the tables do not match the header
+ /// Thrown if the tables do not match the header
///
private void ProcessDqt(int remaining)
{
@@ -1061,11 +1160,11 @@ namespace ImageSharp.Formats
}
remaining -= Block8x8F.ScalarCount;
- this.ReadFull(this.temp, 0, Block8x8F.ScalarCount);
+ this.ReadFull(this.Temp, 0, Block8x8F.ScalarCount);
for (int i = 0; i < Block8x8F.ScalarCount; i++)
{
- this.quantizationTables[tq][i] = this.temp[i];
+ this.QuantizationTables[tq][i] = this.Temp[i];
}
break;
@@ -1077,11 +1176,11 @@ namespace ImageSharp.Formats
}
remaining -= 2 * Block8x8F.ScalarCount;
- this.ReadFull(this.temp, 0, 2 * Block8x8F.ScalarCount);
+ this.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount);
for (int i = 0; i < Block8x8F.ScalarCount; i++)
{
- this.quantizationTables[tq][i] = (this.temp[2 * i] << 8) | this.temp[(2 * i) + 1];
+ this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1];
}
break;
@@ -1102,1159 +1201,288 @@ namespace ImageSharp.Formats
}
///
- /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in macroblocks
+ /// Processes the Start of Frame marker. Specified in section B.2.2.
///
/// The remaining bytes in the segment block.
- private void ProcessDefineRestartIntervalMarker(int remaining)
+ private void ProcessStartOfFrameMarker(int remaining)
{
- if (remaining != 2)
+ if (this.ComponentCount != 0)
{
- throw new ImageFormatException("DRI has wrong length");
+ throw new ImageFormatException("Multiple SOF markers");
}
- this.ReadFull(this.temp, 0, 2);
- this.restartInterval = ((int)this.temp[0] << 8) + (int)this.temp[1];
- }
-
- ///
- /// Processes the application header containing the JFIF identifier plus extra data.
- ///
- /// The remaining bytes in the segment block.
- private void ProcessApplicationHeader(int remaining)
- {
- if (remaining < 5)
+ switch (remaining)
{
- this.Skip(remaining);
- return;
+ case 6 + (3 * 1): // Grayscale image.
+ this.ComponentCount = 1;
+ break;
+ case 6 + (3 * 3): // YCbCr or RGB image.
+ this.ComponentCount = 3;
+ break;
+ case 6 + (3 * 4): // YCbCrK or CMYK image.
+ this.ComponentCount = 4;
+ break;
+ default:
+ throw new ImageFormatException("Incorrect number of components");
}
- this.ReadFull(this.temp, 0, 13);
- remaining -= 13;
-
- // TODO: We should be using constants for this.
- this.isJfif = this.temp[0] == 'J' && this.temp[1] == 'F' && this.temp[2] == 'I' && this.temp[3] == 'F'
- && this.temp[4] == '\x00';
+ this.ReadFull(this.Temp, 0, remaining);
- if (this.isJfif)
+ // We only support 8-bit precision.
+ if (this.Temp[0] != 8)
{
- this.horizontalResolution = (short)(this.temp[9] + (this.temp[10] << 8));
- this.verticalResolution = (short)(this.temp[11] + (this.temp[12] << 8));
+ throw new ImageFormatException("Only 8-Bit precision supported.");
}
- if (remaining > 0)
+ this.ImageHeight = (this.Temp[1] << 8) + this.Temp[2];
+ this.ImageWidth = (this.Temp[3] << 8) + this.Temp[4];
+ if (this.Temp[5] != this.ComponentCount)
{
- this.Skip(remaining);
+ throw new ImageFormatException("SOF has wrong length");
}
- }
- ///
- /// Processes the App1 marker retrieving any stored metadata
- ///
- /// The pixel format.
- /// The remaining bytes in the segment block.
- /// The image.
- private void ProcessApp1Marker(int remaining, Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- if (remaining < 6)
- {
- this.Skip(remaining);
- return;
- }
-
- byte[] profile = new byte[remaining];
- this.ReadFull(profile, 0, remaining);
-
- if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0'
- && profile[5] == '\0')
- {
- image.ExifProfile = new ExifProfile(profile);
- }
- }
-
- ///
- /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters.
- /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not
- /// deleted by default when deleting all metadata because it may affect the appearance of the image.
- ///
- /// The remaining number of bytes in the stream.
- private void ProcessApp14Marker(int remaining)
- {
- if (remaining < 12)
- {
- this.Skip(remaining);
- return;
- }
-
- this.ReadFull(this.temp, 0, 12);
- remaining -= 12;
-
- if (this.temp[0] == 'A' && this.temp[1] == 'd' && this.temp[2] == 'o' && this.temp[3] == 'b'
- && this.temp[4] == 'e')
- {
- this.adobeTransformValid = true;
- this.adobeTransform = this.temp[11];
- }
-
- if (remaining > 0)
- {
- this.Skip(remaining);
- }
- }
-
- ///
- /// Converts the image from the original YCCK image pixels.
- ///
- /// The pixel format.
- /// The image width.
- /// The image height.
- /// The image.
- private void ConvertFromYcck(int width, int height, Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
-
- image.InitPixels(width, height);
-
- using (PixelAccessor pixels = image.Lock())
- {
- Parallel.For(
- 0,
- height,
- y =>
- {
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte yy = this.ycbcrImage.YChannel[yo + x];
- byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
- byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
-
- TColor packed = default(TColor);
- this.PackYcck(ref packed, yy, cb, cr, x, y);
- pixels[x, y] = packed;
- }
- });
- }
-
- this.AssignResolution(image);
- }
-
- ///
- /// Converts the image from the original CMYK image pixels.
- ///
- /// The pixel format.
- /// The image width.
- /// The image height.
- /// The image.
- private void ConvertFromCmyk(int width, int height, Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
-
- image.InitPixels(width, height);
-
- using (PixelAccessor pixels = image.Lock())
- {
- Parallel.For(
- 0,
- height,
- y =>
- {
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte cyan = this.ycbcrImage.YChannel[yo + x];
- byte magenta = this.ycbcrImage.CbChannel[co + (x / scale)];
- byte yellow = this.ycbcrImage.CrChannel[co + (x / scale)];
-
- TColor packed = default(TColor);
- this.PackCmyk(ref packed, cyan, magenta, yellow, x, y);
- pixels[x, y] = packed;
- }
- });
- }
-
- this.AssignResolution(image);
- }
-
- ///
- /// Converts the image from the original grayscale image pixels.
- ///
- /// The pixel format.
- /// The image width.
- /// The image height.
- /// The image.
- private void ConvertFromGrayScale(int width, int height, Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- image.InitPixels(width, height);
-
- using (PixelAccessor pixels = image.Lock())
- {
- Parallel.For(
- 0,
- height,
- Bootstrapper.ParallelOptions,
- y =>
- {
- int yoff = this.grayImage.GetRowOffset(y);
- for (int x = 0; x < width; x++)
- {
- byte rgb = this.grayImage.Pixels[yoff + x];
-
- TColor packed = default(TColor);
- packed.PackFromBytes(rgb, rgb, rgb, 255);
- pixels[x, y] = packed;
- }
- });
- }
-
- this.AssignResolution(image);
- }
-
- ///
- /// Converts the image from the original YCbCr image pixels.
- ///
- /// The pixel format.
- /// The image width.
- /// The image height.
- /// The image.
- private void ConvertFromYCbCr(int width, int height, Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
- image.InitPixels(width, height);
-
- using (PixelAccessor pixels = image.Lock())
- {
- Parallel.For(
- 0,
- height,
- Bootstrapper.ParallelOptions,
- y =>
- {
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte yy = this.ycbcrImage.YChannel[yo + x];
- byte cb = this.ycbcrImage.CbChannel[co + (x / scale)];
- byte cr = this.ycbcrImage.CrChannel[co + (x / scale)];
-
- TColor packed = default(TColor);
- PackYcbCr(ref packed, yy, cb, cr);
- pixels[x, y] = packed;
- }
- });
- }
-
- this.AssignResolution(image);
- }
-
- ///
- /// Converts the image from the original RBG image pixels.
- ///
- /// The pixel format.
- /// The image width.
- /// The height.
- /// The image.
- private void ConvertFromRGB(int width, int height, Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- int scale = this.componentArray[0].HorizontalFactor / this.componentArray[1].HorizontalFactor;
- image.InitPixels(width, height);
-
- using (PixelAccessor pixels = image.Lock())
+ for (int i = 0; i < this.ComponentCount; i++)
{
- Parallel.For(
- 0,
- height,
- Bootstrapper.ParallelOptions,
- y =>
- {
- int yo = this.ycbcrImage.GetRowYOffset(y);
- int co = this.ycbcrImage.GetRowCOffset(y);
-
- for (int x = 0; x < width; x++)
- {
- byte red = this.ycbcrImage.YChannel[yo + x];
- byte green = this.ycbcrImage.CbChannel[co + (x / scale)];
- byte blue = this.ycbcrImage.CrChannel[co + (x / scale)];
-
- TColor packed = default(TColor);
- packed.PackFromBytes(red, green, blue, 255);
- pixels[x, y] = packed;
- }
- });
- }
+ this.ComponentArray[i].Identifier = this.Temp[6 + (3 * i)];
- this.AssignResolution(image);
- }
-
- ///
- /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header.
- ///
- /// The pixel format.
- /// The image to assign the resolution to.
- private void AssignResolution(Image image)
- where TColor : struct, IPackedPixel, IEquatable
- {
- if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0)
- {
- image.HorizontalResolution = this.horizontalResolution;
- image.VerticalResolution = this.verticalResolution;
- }
- }
-
- ///
- /// Processes the SOS (Start of scan marker).
- ///
- ///
- /// TODO: This also needs some significant refactoring to follow a more OO format.
- ///
- /// The remaining bytes in the segment block.
- ///
- /// Missing SOF Marker
- /// SOS has wrong length
- ///
- private void ProcessStartOfScan(int remaining)
- {
- if (this.componentCount == 0)
- {
- throw new ImageFormatException("Missing SOF marker");
- }
-
- if (remaining < 6 || 4 + (2 * this.componentCount) < remaining || remaining % 2 != 0)
- {
- throw new ImageFormatException("SOS has wrong length");
- }
-
- this.ReadFull(this.temp, 0, remaining);
- byte scanComponentCount = this.temp[0];
-
- int scanComponentCountX2 = 2 * scanComponentCount;
- if (remaining != 4 + scanComponentCountX2)
- {
- throw new ImageFormatException("SOS length inconsistent with number of components");
- }
-
- Scan[] scan = new Scan[MaxComponents];
- int totalHv = 0;
-
- for (int i = 0; i < scanComponentCount; i++)
- {
- this.ProcessScanImpl(i, ref scan[i], scan, ref totalHv);
- }
-
- // Section B.2.3 states that if there is more than one component then the
- // total H*V values in a scan must be <= 10.
- if (this.componentCount > 1 && totalHv > 10)
- {
- throw new ImageFormatException("Total sampling factors too large.");
- }
-
- // zigStart and zigEnd are the spectral selection bounds.
- // ah and al are the successive approximation high and low values.
- // The spec calls these values Ss, Se, Ah and Al.
- // For progressive JPEGs, these are the two more-or-less independent
- // aspects of progression. Spectral selection progression is when not
- // all of a block's 64 DCT coefficients are transmitted in one pass.
- // For example, three passes could transmit coefficient 0 (the DC
- // component), coefficients 1-5, and coefficients 6-63, in zig-zag
- // order. Successive approximation is when not all of the bits of a
- // band of coefficients are transmitted in one pass. For example,
- // three passes could transmit the 6 most significant bits, followed
- // by the second-least significant bit, followed by the least
- // significant bit.
- // For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
- int zigStart = 0;
- int zigEnd = Block8x8F.ScalarCount - 1;
- int ah = 0;
- int al = 0;
-
- if (this.isProgressive)
- {
- zigStart = this.temp[1 + scanComponentCountX2];
- zigEnd = this.temp[2 + scanComponentCountX2];
- ah = this.temp[3 + scanComponentCountX2] >> 4;
- al = this.temp[3 + scanComponentCountX2] & 0x0f;
-
- if ((zigStart == 0 && zigEnd != 0) || zigStart > zigEnd || zigEnd >= Block8x8F.ScalarCount)
- {
- throw new ImageFormatException("Bad spectral selection bounds");
- }
-
- if (zigStart != 0 && scanComponentCount != 1)
- {
- throw new ImageFormatException("Progressive AC coefficients for more than one component");
- }
-
- if (ah != 0 && ah != al + 1)
- {
- throw new ImageFormatException("Bad successive approximation values");
- }
- }
-
- // mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
- int h0 = this.componentArray[0].HorizontalFactor;
- int v0 = this.componentArray[0].VerticalFactor;
- int mxx = (this.imageWidth + (8 * h0) - 1) / (8 * h0);
- int myy = (this.imageHeight + (8 * v0) - 1) / (8 * v0);
-
- if (this.grayImage == null && this.ycbcrImage == null)
- {
- this.MakeImage(mxx, myy);
- }
-
- if (this.isProgressive)
- {
- for (int i = 0; i < scanComponentCount; i++)
- {
- int compIndex = scan[i].Index;
- if (this.progCoeffs[compIndex] == null)
- {
- int size = mxx * myy * this.componentArray[compIndex].HorizontalFactor
- * this.componentArray[compIndex].VerticalFactor;
-
- this.progCoeffs[compIndex] = new Block8x8F[size];
- }
- }
- }
-
- this.bits = default(Bits);
-
- int mcu = 0;
- byte expectedRst = JpegConstants.Markers.RST0;
-
- // b is the decoded coefficients block, in natural (not zig-zag) order.
- // Block b;
- int[] dc = new int[MaxComponents];
-
- // bx and by are the location of the current block, in units of 8x8
- // blocks: the third block in the first row has (bx, by) = (2, 0).
- int bx, by, blockCount = 0;
-
- Block8x8F b = default(Block8x8F);
- Block8x8F temp1 = default(Block8x8F);
- Block8x8F temp2 = default(Block8x8F);
-
- UnzigData unzig = UnzigData.Create();
-
- int* unzigPtr = unzig.Data;
-
- for (int my = 0; my < myy; my++)
- {
- for (int mx = 0; mx < mxx; mx++)
+ // Section B.2.2 states that "the value of C_i shall be different from
+ // the values of C_1 through C_(i-1)".
+ for (int j = 0; j < i; j++)
{
- for (int i = 0; i < scanComponentCount; i++)
- {
- int compIndex = scan[i].Index;
- int hi = this.componentArray[compIndex].HorizontalFactor;
- int vi = this.componentArray[compIndex].VerticalFactor;
-
- for (int j = 0; j < hi * vi; j++)
- {
- // The blocks are traversed one MCU at a time. For 4:2:0 chroma
- // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
- // For a baseline 32x16 pixel image, the Y blocks visiting order is:
- // 0 1 4 5
- // 2 3 6 7
- // For progressive images, the interleaved scans (those with component count > 1)
- // are traversed as above, but non-interleaved scans are traversed left
- // to right, top to bottom:
- // 0 1 2 3
- // 4 5 6 7
- // Only DC scans (zigStart == 0) can be interleave AC scans must have
- // only one component.
- // To further complicate matters, for non-interleaved scans, there is no
- // data for any blocks that are inside the image at the MCU level but
- // outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
- // progressive image consists of two 16x16 MCUs. The interleaved scans
- // will process 8 Y blocks:
- // 0 1 4 5
- // 2 3 6 7
- // The non-interleaved scans will process only 6 Y blocks:
- // 0 1 2
- // 3 4 5
- if (scanComponentCount != 1)
- {
- bx = (hi * mx) + (j % hi);
- by = (vi * my) + (j / hi);
- }
- else
- {
- int q = mxx * hi;
- bx = blockCount % q;
- by = blockCount / q;
- blockCount++;
- if (bx * 8 >= this.imageWidth || by * 8 >= this.imageHeight)
- {
- continue;
- }
- }
-
- int qtIndex = this.componentArray[compIndex].Selector;
-
- // TODO: Find a way to clean up this mess
- fixed (Block8x8F* qtp = &this.quantizationTables[qtIndex])
- {
- // Load the previous partially decoded coefficients, if applicable.
- if (this.isProgressive)
- {
- this.blockIndex = ((@by * mxx) * hi) + bx;
-
- fixed (Block8x8F* bp = &this.progCoeffs[compIndex][this.blockIndex])
- {
- this.ProcessBlockImpl(
- ah,
- bp,
- &temp1,
- &temp2,
- unzigPtr,
- scan,
- i,
- zigStart,
- zigEnd,
- al,
- dc,
- compIndex,
- @by,
- mxx,
- hi,
- bx,
- qtp);
- }
- }
- else
- {
- b.Clear();
- this.ProcessBlockImpl(
- ah,
- &b,
- &temp1,
- &temp2,
- unzigPtr,
- scan,
- i,
- zigStart,
- zigEnd,
- al,
- dc,
- compIndex,
- @by,
- mxx,
- hi,
- bx,
- qtp);
- }
- }
- }
-
- // for j
- }
-
- // for i
- mcu++;
-
- if (this.restartInterval > 0 && mcu % this.restartInterval == 0 && mcu < mxx * myy)
+ if (this.ComponentArray[i].Identifier == this.ComponentArray[j].Identifier)
{
- // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
- // but this one assumes well-formed input, and hence the restart marker follows immediately.
- this.ReadFull(this.temp, 0, 2);
- if (this.temp[0] != 0xff || this.temp[1] != expectedRst)
- {
- throw new ImageFormatException("Bad RST marker");
- }
-
- expectedRst++;
- if (expectedRst == JpegConstants.Markers.RST7 + 1)
- {
- expectedRst = JpegConstants.Markers.RST0;
- }
-
- // Reset the Huffman decoder.
- this.bits = default(Bits);
-
- // Reset the DC components, as per section F.2.1.3.1.
- dc = new int[MaxComponents];
-
- // Reset the progressive decoder state, as per section G.1.2.2.
- this.eobRun = 0;
+ throw new ImageFormatException("Repeated component identifier");
}
}
- // for mx
- }
-
- // for my
- }
-
- private void ProcessBlockImpl(
- int ah,
- Block8x8F* b,
- Block8x8F* temp1,
- Block8x8F* temp2,
- int* unzigPtr,
- Scan[] scan,
- int i,
- int zigStart,
- int zigEnd,
- int al,
- int[] dc,
- int compIndex,
- int @by,
- int mxx,
- int hi,
- int bx,
- Block8x8F* qt)
- {
- int huffmannIdx = (AcTable * ThRowSize) + scan[i].AcTableSelector;
- if (ah != 0)
- {
- this.Refine(b, ref this.huffmanTrees[huffmannIdx], unzigPtr, zigStart, zigEnd, 1 << al);
- }
- else
- {
- int zig = zigStart;
- if (zig == 0)
+ this.ComponentArray[i].Selector = this.Temp[8 + (3 * i)];
+ if (this.ComponentArray[i].Selector > MaxTq)
{
- zig++;
-
- // Decode the DC coefficient, as specified in section F.2.2.1.
- byte value = this.DecodeHuffman(
- ref this.huffmanTrees[(DcTable * ThRowSize) + scan[i].DcTableSelector]);
- if (value > 16)
- {
- throw new ImageFormatException("Excessive DC component");
- }
-
- int deltaDC = this.bits.ReceiveExtend(value, this);
- dc[compIndex] += deltaDC;
-
- // b[0] = dc[compIndex] << al;
- Block8x8F.SetScalarAt(b, 0, dc[compIndex] << al);
+ throw new ImageFormatException("Bad Tq value");
}
- if (zig <= zigEnd && this.eobRun > 0)
- {
- this.eobRun--;
- }
- else
+ byte hv = this.Temp[7 + (3 * i)];
+ int h = hv >> 4;
+ int v = hv & 0x0f;
+ if (h < 1 || h > 4 || v < 1 || v > 4)
{
- // Decode the AC coefficients, as specified in section F.2.2.2.
- // Huffman huffv = ;
- for (; zig <= zigEnd; zig++)
- {
- byte value = this.DecodeHuffman(ref this.huffmanTrees[huffmannIdx]);
- byte val0 = (byte)(value >> 4);
- byte val1 = (byte)(value & 0x0f);
- if (val1 != 0)
- {
- zig += val0;
- if (zig > zigEnd)
- {
- break;
- }
-
- int ac = this.bits.ReceiveExtend(val1, this);
-
- // b[Unzig[zig]] = ac << al;
- Block8x8F.SetScalarAt(b, unzigPtr[zig], ac << al);
- }
- else
- {
- if (val0 != 0x0f)
- {
- this.eobRun = (ushort)(1 << val0);
- if (val0 != 0)
- {
- this.eobRun |= (ushort)this.DecodeBits(val0);
- }
-
- this.eobRun--;
- break;
- }
-
- zig += 0x0f;
- }
- }
+ throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio");
}
- }
- if (this.isProgressive)
- {
- if (zigEnd != Block8x8F.ScalarCount - 1 || al != 0)
+ if (h == 3 || v == 3)
{
- // We haven't completely decoded this 8x8 block. Save the coefficients.
-
- // TODO!!!
- // throw new NotImplementedException();
- // this.progCoeffs[compIndex][((@by * mxx) * hi) + bx] = b.Clone();
- this.progCoeffs[compIndex][((@by * mxx) * hi) + bx] = *b;
-
- // At this point, we could execute the rest of the loop body to dequantize and
- // perform the inverse DCT, to save early stages of a progressive image to the
- // *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
- // the jpeg.Decode function does not return until the entire image is decoded,
- // so we "continue" here to avoid wasted computation.
- return;
+ throw new ImageFormatException("Lnsupported subsampling ratio");
}
- }
-
- // Dequantize, perform the inverse DCT and store the block to the image.
- Block8x8F.UnZig(b, qt, unzigPtr);
- DCT.TransformIDCT(ref *b, ref *temp1, ref *temp2);
-
- byte[] dst;
- int offset;
- int stride;
-
- if (this.componentCount == 1)
- {
- dst = this.grayImage.Pixels;
- stride = this.grayImage.Stride;
- offset = this.grayImage.Offset + (8 * ((@by * this.grayImage.Stride) + bx));
- }
- else
- {
- switch (compIndex)
+ switch (this.ComponentCount)
{
- case 0:
- dst = this.ycbcrImage.YChannel;
- stride = this.ycbcrImage.YStride;
- offset = this.ycbcrImage.YOffset + (8 * ((@by * this.ycbcrImage.YStride) + bx));
- break;
-
case 1:
- dst = this.ycbcrImage.CbChannel;
- stride = this.ycbcrImage.CStride;
- offset = this.ycbcrImage.COffset + (8 * ((@by * this.ycbcrImage.CStride) + bx));
- break;
-
- case 2:
- dst = this.ycbcrImage.CrChannel;
- stride = this.ycbcrImage.CStride;
- offset = this.ycbcrImage.COffset + (8 * ((@by * this.ycbcrImage.CStride) + bx));
- break;
-
- case 3:
- dst = this.blackPixels;
- stride = this.blackStride;
- offset = 8 * ((@by * this.blackStride) + bx);
+ // If a JPEG image has only one component, section A.2 says "this data
+ // is non-interleaved by definition" and section A.2.2 says "[in this
+ // case...] the order of data units within a scan shall be left-to-right
+ // and top-to-bottom... regardless of the values of H_1 and V_1". Section
+ // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be
+ // one data unit". Similarly, section A.1.1 explains that it is the ratio
+ // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale
+ // images, H_1 is the maximum H_j for all components j, so that ratio is
+ // always 1. The component's (h, v) is effectively always (1, 1): even if
+ // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8
+ // MCUs, not two 16x8 MCUs.
+ h = 1;
+ v = 1;
break;
- default:
- throw new ImageFormatException("Too many components");
- }
- }
-
- // Level shift by +128, clip to [0, 255], and write to dst.
- temp1->CopyColorsTo(new MutableSpan(dst, offset), stride, temp2);
- }
-
- private void ProcessScanImpl(int i, ref Scan currentScan, Scan[] scan, ref int totalHv)
- {
- // Component selector.
- int cs = this.temp[1 + (2 * i)];
- int compIndex = -1;
- for (int j = 0; j < this.componentCount; j++)
- {
- // Component compv = ;
- if (cs == this.componentArray[j].Identifier)
- {
- compIndex = j;
- }
- }
-
- if (compIndex < 0)
- {
- throw new ImageFormatException("Unknown component selector");
- }
-
- currentScan.Index = (byte)compIndex;
-
- this.ProcessComponentImpl(i, ref currentScan, scan, ref totalHv, ref this.componentArray[compIndex]);
- }
-
- private void ProcessComponentImpl(
- int i,
- ref Scan currentScan,
- Scan[] scan,
- ref int totalHv,
- ref Component currentComponent)
- {
- // Section B.2.3 states that "the value of Cs_j shall be different from
- // the values of Cs_1 through Cs_(j-1)". Since we have previously
- // verified that a frame's component identifiers (C_i values in section
- // B.2.2) are unique, it suffices to check that the implicit indexes
- // into comp are unique.
- for (int j = 0; j < i; j++)
- {
- if (currentScan.Index == scan[j].Index)
- {
- throw new ImageFormatException("Repeated component selector");
- }
- }
-
- totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor;
-
- currentScan.DcTableSelector = (byte)(this.temp[2 + (2 * i)] >> 4);
- if (currentScan.DcTableSelector > MaxTh)
- {
- throw new ImageFormatException("Bad DC table selector value");
- }
-
- currentScan.AcTableSelector = (byte)(this.temp[2 + (2 * i)] & 0x0f);
- if (currentScan.AcTableSelector > MaxTh)
- {
- throw new ImageFormatException("Bad AC table selector value");
- }
- }
-
- ///
- /// Decodes a successive approximation refinement block, as specified in section G.1.2.
- ///
- /// The block of coefficients
- /// The Huffman tree
- /// Unzig ptr
- /// The zig-zag start index
- /// The zig-zag end index
- /// The low transform offset
- private void Refine(Block8x8F* b, ref HuffmanTree h, int* unzigPtr, int zigStart, int zigEnd, int delta)
- {
- // Refining a DC component is trivial.
- if (zigStart == 0)
- {
- if (zigEnd != 0)
- {
- throw new ImageFormatException("Invalid state for zig DC component");
- }
-
- bool bit = this.DecodeBit();
- if (bit)
- {
- int stuff = (int)Block8x8F.GetScalarAt(b, 0);
+ case 3:
- // int stuff = (int)b[0];
- stuff |= delta;
+ // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0,
+ // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the
+ // (h, v) values for the Y component are either (1, 1), (1, 2),
+ // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values
+ // must be a multiple of the Cb and Cr component's values. We also
+ // assume that the two chroma components have the same subsampling
+ // ratio.
+ switch (i)
+ {
+ case 0:
+ {
+ // Y.
+ // We have already verified, above, that h and v are both
+ // either 1, 2 or 4, so invalid (h, v) combinations are those
+ // with v == 4.
+ if (v == 4)
+ {
+ throw new ImageFormatException("Unsupported subsampling ratio");
+ }
- // b[0] = stuff;
- Block8x8F.SetScalarAt(b, 0, stuff);
- }
+ break;
+ }
- return;
- }
+ case 1:
+ {
+ // Cb.
+ if (this.ComponentArray[0].HorizontalFactor % h != 0
+ || this.ComponentArray[0].VerticalFactor % v != 0)
+ {
+ throw new ImageFormatException("Unsupported subsampling ratio");
+ }
- // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
- int zig = zigStart;
- if (this.eobRun == 0)
- {
- for (; zig <= zigEnd; zig++)
- {
- bool done = false;
- int z = 0;
- byte val = this.DecodeHuffman(ref h);
- int val0 = val >> 4;
- int val1 = val & 0x0f;
+ break;
+ }
- switch (val1)
- {
- case 0:
- if (val0 != 0x0f)
- {
- this.eobRun = (ushort)(1 << val0);
- if (val0 != 0)
+ case 2:
{
- this.eobRun |= (ushort)this.DecodeBits(val0);
+ // Cr.
+ if (this.ComponentArray[1].HorizontalFactor != h
+ || this.ComponentArray[1].VerticalFactor != v)
+ {
+ throw new ImageFormatException("Unsupported subsampling ratio");
+ }
+
+ break;
}
+ }
- done = true;
- }
+ break;
- break;
- case 1:
- z = delta;
- bool bit = this.DecodeBit();
- if (!bit)
- {
- z = -z;
- }
+ case 4:
- break;
- default:
- throw new ImageFormatException("Unexpected Huffman code");
- }
+ // For 4-component images (either CMYK or YCbCrK), we only support two
+ // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22].
+ // Theoretically, 4-component JPEG images could mix and match hv values
+ // but in practice, those two combinations are the only ones in use,
+ // and it simplifies the applyBlack code below if we can assume that:
+ // - for CMYK, the C and K channels have full samples, and if the M
+ // and Y channels subsample, they subsample both horizontally and
+ // vertically.
+ // - for YCbCrK, the Y and K channels have full samples.
+ switch (i)
+ {
+ case 0:
+ if (hv != 0x11 && hv != 0x22)
+ {
+ throw new ImageFormatException("Unsupported subsampling ratio");
+ }
- if (done)
- {
- break;
- }
+ break;
+ case 1:
+ case 2:
+ if (hv != 0x11)
+ {
+ throw new ImageFormatException("Unsupported subsampling ratio");
+ }
- int blah = zig;
+ break;
+ case 3:
+ if (this.ComponentArray[0].HorizontalFactor != h
+ || this.ComponentArray[0].VerticalFactor != v)
+ {
+ throw new ImageFormatException("Unsupported subsampling ratio");
+ }
- zig = this.RefineNonZeroes(b, zig, zigEnd, val0, delta, unzigPtr);
- if (zig > zigEnd)
- {
- throw new ImageFormatException($"Too many coefficients {zig} > {zigEnd}");
- }
+ break;
+ }
- if (z != 0)
- {
- // b[Unzig[zig]] = z;
- Block8x8F.SetScalarAt(b, unzigPtr[zig], z);
- }
+ break;
}
- }
- if (this.eobRun > 0)
- {
- this.eobRun--;
- this.RefineNonZeroes(b, zig, zigEnd, -1, delta, unzigPtr);
+ this.ComponentArray[i].HorizontalFactor = h;
+ this.ComponentArray[i].VerticalFactor = v;
}
}
///
- /// Refines non-zero entries of b in zig-zag order.
- /// If >= 0, the first zero entries are skipped over.
+ /// Processes the SOS (Start of scan marker).
///
- /// The block of coefficients
- /// The zig-zag start index
- /// The zig-zag end index
- /// The non-zero entry
- /// The low transform offset
- /// Pointer to the Jpeg Unzig data (data part of )
- /// The
- private int RefineNonZeroes(Block8x8F* b, int zig, int zigEnd, int nz, int delta, int* unzigPtr)
+ /// The remaining bytes in the segment block.
+ ///
+ /// Missing SOF Marker
+ /// SOS has wrong length
+ ///
+ private void ProcessStartOfScan(int remaining)
{
- for (; zig <= zigEnd; zig++)
- {
- int u = unzigPtr[zig];
- float bu = Block8x8F.GetScalarAt(b, u);
-
- // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
- if (bu == 0)
- {
- if (nz == 0)
- {
- break;
- }
-
- nz--;
- continue;
- }
-
- bool bit = this.DecodeBit();
- if (!bit)
- {
- continue;
- }
-
- if (bu >= 0)
- {
- // b[u] += delta;
- Block8x8F.SetScalarAt(b, u, bu + delta);
- }
- else
- {
- // b[u] -= delta;
- Block8x8F.SetScalarAt(b, u, bu - delta);
- }
- }
-
- return zig;
+ JpegScanDecoder scan = default(JpegScanDecoder);
+ JpegScanDecoder.Init(&scan, this, remaining);
+ this.Bits = default(Bits);
+ this.MakeImage(scan.XNumberOfMCUs, scan.YNumberOfMCUs);
+ scan.ProcessBlocks(this);
}
///
- /// Makes the image from the buffer.
+ /// Skips the next n bytes.
///
- /// The horizontal MCU count
- /// The vertical MCU count
- private void MakeImage(int mxx, int myy)
+ /// The number of bytes to ignore.
+ private void Skip(int count)
{
- if (this.componentCount == 1)
+ // Unread the overshot bytes, if any.
+ if (this.bytes.UnreadableBytes != 0)
{
- GrayImage gray = new GrayImage(8 * mxx, 8 * myy);
- this.grayImage = gray.Subimage(0, 0, this.imageWidth, this.imageHeight);
+ if (this.Bits.UnreadBits >= 8)
+ {
+ this.UnreadByteStuffedByte();
+ }
+
+ this.bytes.UnreadableBytes = 0;
}
- else
- {
- int h0 = this.componentArray[0].HorizontalFactor;
- int v0 = this.componentArray[0].VerticalFactor;
- int horizontalRatio = h0 / this.componentArray[1].HorizontalFactor;
- int verticalRatio = v0 / this.componentArray[1].VerticalFactor;
- YCbCrImage.YCbCrSubsampleRatio ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444;
- switch ((horizontalRatio << 4) | verticalRatio)
+ while (true)
+ {
+ int m = this.bytes.J - this.bytes.I;
+ if (m > count)
{
- case 0x11:
- ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444;
- break;
- case 0x12:
- ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440;
- break;
- case 0x21:
- ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422;
- break;
- case 0x22:
- ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420;
- break;
- case 0x41:
- ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411;
- break;
- case 0x42:
- ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410;
- break;
+ m = count;
}
- YCbCrImage ycbcr = new YCbCrImage(8 * h0 * mxx, 8 * v0 * myy, ratio);
- this.ycbcrImage = ycbcr.Subimage(0, 0, this.imageWidth, this.imageHeight);
-
- if (this.componentCount == 4)
+ this.bytes.I += m;
+ count -= m;
+ if (count == 0)
{
- int h3 = this.componentArray[3].HorizontalFactor;
- int v3 = this.componentArray[3].VerticalFactor;
- this.blackPixels = new byte[8 * h3 * mxx * 8 * v3 * myy];
- this.blackStride = 8 * h3 * mxx;
+ break;
}
+
+ this.bytes.Fill(this.inputStream);
}
}
///
- /// Returns a value indicating whether the image in an RGB image.
+ /// Undoes the most recent ReadByteStuffedByte call,
+ /// giving a byte of data back from bits to bytes. The Huffman look-up table
+ /// requires at least 8 bits for look-up, which means that Huffman decoding can
+ /// sometimes overshoot and read one or two too many bytes. Two-byte overshoot
+ /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte.
///
- ///
- /// The .
- ///
- private bool IsRGB()
+ private void UnreadByteStuffedByte()
{
- if (this.isJfif)
- {
- return false;
- }
-
- if (this.adobeTransformValid && this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown)
+ this.bytes.I -= this.bytes.UnreadableBytes;
+ this.bytes.UnreadableBytes = 0;
+ if (this.Bits.UnreadBits >= 8)
{
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
- // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr.
- return true;
+ this.Bits.Accumulator >>= 8;
+ this.Bits.UnreadBits -= 8;
+ this.Bits.Mask >>= 8;
}
-
- return this.componentArray[0].Identifier == 'R' && this.componentArray[1].Identifier == 'G'
- && this.componentArray[2].Identifier == 'B';
- }
-
- ///
- /// Optimized method to pack bytes to the image from the YCCK color space.
- /// This is faster than implicit casting as it avoids double packing.
- ///
- /// The pixel format.
- /// The packed pixel.
- /// The y luminance component.
- /// The cb chroma component.
- /// The cr chroma component.
- /// The x-position within the image.
- /// The y-position within the image.
- private void PackYcck(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy)
- where TColor : struct, IPackedPixel, IEquatable
- {
- // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get
- // CMY, and patch in the original K. The RGB to CMY inversion cancels
- // out the 'Adobe inversion' described in the applyBlack doc comment
- // above, so in practice, only the fourth channel (black) is inverted.
- // TODO: We can speed this up further with Vector4
- int ccb = cb - 128;
- int ccr = cr - 128;
-
- // First convert from YCbCr to CMY
- float cyan = (y + (1.402F * ccr)).Clamp(0, 255) / 255F;
- float magenta = (y - (0.34414F * ccb) - (0.71414F * ccr)).Clamp(0, 255) / 255F;
- float yellow = (y + (1.772F * ccb)).Clamp(0, 255) / 255F;
-
- // Get keyline
- float keyline = (255 - this.blackPixels[(yy * this.blackStride) + xx]) / 255F;
-
- // Convert back to RGB
- byte r = (byte)(((1 - cyan) * (1 - keyline)).Clamp(0, 1) * 255);
- byte g = (byte)(((1 - magenta) * (1 - keyline)).Clamp(0, 1) * 255);
- byte b = (byte)(((1 - yellow) * (1 - keyline)).Clamp(0, 1) * 255);
-
- packed.PackFromBytes(r, g, b, 255);
- }
-
- ///
- /// Optimized method to pack bytes to the image from the CMYK color space.
- /// This is faster than implicit casting as it avoids double packing.
- ///
- /// The pixel format.
- /// The packed pixel.
- /// The cyan component.
- /// The magenta component.
- /// The yellow component.
- /// The x-position within the image.
- /// The y-position within the image.
- private void PackCmyk(ref TColor packed, byte c, byte m, byte y, int xx, int yy)
- where TColor : struct, IPackedPixel, IEquatable
- {
- // Get keyline
- float keyline = (255 - this.blackPixels[(yy * this.blackStride) + xx]) / 255F;
-
- // Convert back to RGB. CMY are not inverted
- byte r = (byte)(((c / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
- byte g = (byte)(((m / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
- byte b = (byte)(((y / 255F) * (1F - keyline)).Clamp(0, 1) * 255);
-
- packed.PackFromBytes(r, g, b, 255);
}
///
- /// Represents a component scan
+ /// The EOF (End of File exception).
+ /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
///
- private struct Scan
+ internal class EOFException : Exception
{
- ///
- /// Gets or sets the component index.
- ///
- public byte Index { get; set; }
-
- ///
- /// Gets or sets the DC table selector
- ///
- public byte DcTableSelector { get; set; }
-
- ///
- /// Gets or sets the AC table selector
- ///
- public byte AcTableSelector { get; set; }
}
///
- /// The missing ff00 exception.
+ /// The missing ff00 exception.
///
internal class MissingFF00Exception : Exception
{
}
///
- /// The EOF (End of File exception).
- /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker
- ///
- internal class EOFException : Exception
- {
- }
-
- ///
- /// The short huffman data exception.
+ /// The short huffman data exception.
///
private class ShortHuffmanDataException : Exception
{
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index a121a0bc2..cd0ec0af4 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -2,6 +2,7 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
+
namespace ImageSharp.Formats
{
using System;
@@ -230,18 +231,24 @@ namespace ImageSharp.Formats
/// Writes data to "Define Quantization Tables" block for QuantIndex
///
/// The "Define Quantization Tables" block
- /// Offset in dqt
+ /// Offset in "Define Quantization Tables" block
/// The quantization index
- /// The quantazation table to copy data from
- private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q)
+ /// The quantization table to copy data from
+ private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F quant)
{
dqt[offset++] = (byte)i;
for (int j = 0; j < Block8x8F.ScalarCount; j++)
{
- dqt[offset++] = (byte)q[j];
+ dqt[offset++] = (byte)quant[j];
}
}
+ ///
+ /// Initializes quantization table.
+ ///
+ /// The quantization index.
+ /// The scaling factor.
+ /// The quantization table.
private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant)
{
for (int j = 0; j < Block8x8F.ScalarCount; j++)
@@ -429,7 +436,7 @@ namespace ImageSharp.Formats
// ReSharper disable once InconsistentNaming
float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
- using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.XYZ, true))
+ using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz))
{
for (int y = 0; y < pixels.Height; y += 8)
{
@@ -517,7 +524,7 @@ namespace ImageSharp.Formats
/// Temporal block to be used as FDCT Destination
/// Temporal block 2
/// Quantization table
- /// The 8x8 Unzig block ptr
+ /// The 8x8 Unzig block pointer
///
/// The
///
@@ -798,7 +805,7 @@ namespace ImageSharp.Formats
// ReSharper disable once InconsistentNaming
float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
- using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.XYZ, true))
+ using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz))
{
for (int y = 0; y < pixels.Height; y += 16)
{
diff --git a/src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs b/src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs
new file mode 100644
index 000000000..7369d658f
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs
@@ -0,0 +1,30 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+namespace ImageSharp.Formats
+{
+ using System.Buffers;
+
+ ///
+ /// Wraps to always provide arrays initialized with default(T)
+ ///
+ /// The element type
+ internal class CleanPooler
+ {
+ private static readonly ArrayPool Pool = ArrayPool.Create();
+
+ ///
+ /// Rents a clean array
+ ///
+ /// The minimum array length
+ /// A clean array of T
+ public static T[] RentCleanArray(int minimumLength) => Pool.Rent(minimumLength);
+
+ ///
+ /// Retursn array to the pool
+ ///
+ /// The array
+ public static void ReturnArray(T[] array) => Pool.Return(array, true);
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpg/Utils/JpegUtils.cs b/src/ImageSharp/Formats/Jpg/Utils/JpegUtils.cs
index 7608c70c6..7f62058b7 100644
--- a/src/ImageSharp/Formats/Jpg/Utils/JpegUtils.cs
+++ b/src/ImageSharp/Formats/Jpg/Utils/JpegUtils.cs
@@ -27,41 +27,12 @@ namespace ImageSharp.Formats.Jpg
int sourceX)
where TColor : struct, IPackedPixel, IEquatable
{
- pixels.UncheckedCopyTo(dest, sourceY, sourceX);
+ pixels.SafeCopyTo(dest, sourceY, sourceX);
int stretchFromX = pixels.Width - sourceX;
int stretchFromY = pixels.Height - sourceY;
StretchPixels(dest, stretchFromX, stretchFromY);
}
- ///
- /// Copy a region of image into the image destination area. Does not throw when requesting a 0-size copy.
- ///
- /// The pixel type
- /// The source
- /// The destination area.
- /// The source row index.
- /// The source column index.
- ///
- /// Thrown when an unsupported component order value is passed.
- ///
- public static void UncheckedCopyTo(
- this PixelAccessor sourcePixels,
- PixelArea destinationArea,
- int sourceY,
- int sourceX)
- where TColor : struct, IPackedPixel, IEquatable
- {
- // TODO: Code smell! This is exactly the same code PixelArea.CopyTo() starts with!
- int width = Math.Min(destinationArea.Width, sourcePixels.Width - sourceX);
- int height = Math.Min(destinationArea.Height, sourcePixels.Height - sourceY);
- if (width < 1 || height < 1)
- {
- return;
- }
-
- sourcePixels.CopyTo(destinationArea, sourceY, sourceX);
- }
-
///
/// Copy an RGB value
///
@@ -93,7 +64,7 @@ namespace ImageSharp.Formats.Jpg
for (int y = 0; y < fromY; y++)
{
- byte* ptrBase = (byte*)area.DataPointer + (y * area.RowByteCount);
+ byte* ptrBase = (byte*)area.DataPointer + (y * area.RowStride);
for (int x = fromX; x < area.Width; x++)
{
@@ -106,8 +77,8 @@ namespace ImageSharp.Formats.Jpg
for (int y = fromY; y < area.Height; y++)
{
- byte* currBase = (byte*)area.DataPointer + (y * area.RowByteCount);
- byte* prevBase = (byte*)area.DataPointer + ((y - 1) * area.RowByteCount);
+ byte* currBase = (byte*)area.DataPointer + (y * area.RowStride);
+ byte* prevBase = (byte*)area.DataPointer + ((y - 1) * area.RowStride);
for (int x = 0; x < area.Width; x++)
{
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 7d3d23265..78df6c72e 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -286,7 +286,7 @@ namespace ImageSharp.Formats
// Convert the color to YCbCr and store the luminance
// Optionally store the original color alpha.
int offset = x * this.bytesPerPixel;
- pixels[x, row].ToBytes(this.chunkTypeBuffer, 0, ComponentOrder.XYZW);
+ pixels[x, row].ToXyzwBytes(this.chunkTypeBuffer, 0);
byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2]));
for (int i = 0; i < this.bytesPerPixel; i++)
@@ -314,7 +314,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable
{
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
- using (PixelArea pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ))
+ using (PixelArea pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz))
{
pixels.CopyTo(pixelRow, row);
}
@@ -507,7 +507,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
- palette[i].ToBytes(bytes, 0, ComponentOrder.XYZW);
+ palette[i].ToXyzwBytes(bytes, 0);
int alpha = bytes[3];
diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs
index 164b28160..8fa40facc 100644
--- a/src/ImageSharp/Image/ImageBase{TColor}.cs
+++ b/src/ImageSharp/Image/ImageBase{TColor}.cs
@@ -64,7 +64,7 @@ namespace ImageSharp
using (PixelAccessor sourcePixels = other.Lock())
using (PixelAccessor target = this.Lock())
{
- sourcePixels.CopyImage(target);
+ sourcePixels.CopyTo(target);
}
}
diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs
index 178ea5274..991be5aec 100644
--- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs
+++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs
@@ -145,69 +145,40 @@ namespace ImageSharp
}
///
- /// Copies a block of pixels at the specified position.
+ /// Copy an area of pixels to the image.
///
- /// The x-coordinate of the source image.
- /// The y-coordinate of the source image.
- /// The target pixel buffer accessor.
- /// The x-coordinate of the target image.
- /// The y-coordinate of the target image.
- /// The number of pixels to copy
- public void CopyBlock(int sourceX, int sourceY, PixelAccessor target, int targetX, int targetY, int pixelCount)
+ /// The area.
+ /// The target row index.
+ /// The target column index.
+ ///
+ /// Thrown when an unsupported component order value is passed.
+ ///
+ public void CopyFrom(PixelArea area, int targetY, int targetX = 0)
{
- int size = Unsafe.SizeOf();
- byte* sourcePtr = this.pixelsBase + (((sourceY * this.Width) + sourceX) * size);
- byte* targetPtr = target.pixelsBase + (((targetY * target.Width) + targetX) * size);
- uint byteCount = (uint)(pixelCount * size);
-
- Unsafe.CopyBlock(targetPtr, sourcePtr, byteCount);
- }
+ this.CheckCoordinates(area, targetX, targetY);
- ///
- /// Copies an entire image.
- ///
- /// The target pixel buffer accessor.
- public void CopyImage(PixelAccessor target)
- {
- this.CopyBlock(0, 0, target, 0, 0, target.Width * target.Height);
+ this.CopyFrom(area, targetX, targetY, area.Width, area.Height);
}
///
- /// Copied a row of pixels from the image.
+ /// Copy pixels from the image to an area of pixels.
///
/// The area.
- /// The target row index.
- /// The target column index.
+ /// The source row index.
+ /// The source column index.
///
/// Thrown when an unsupported component order value is passed.
///
- public void CopyFrom(PixelArea area, int targetY, int targetX = 0)
+ public void CopyTo(PixelArea area, int sourceY, int sourceX = 0)
{
- int width = Math.Min(area.Width, this.Width - targetX);
- int height = Math.Min(area.Height, this.Height - targetY);
+ this.CheckCoordinates(area, sourceX, sourceY);
- this.CheckDimensions(width, height);
- switch (area.ComponentOrder)
- {
- case ComponentOrder.ZYX:
- this.CopyFromZYX(area, targetY, targetX, width, height);
- break;
- case ComponentOrder.ZYXW:
- this.CopyFromZYXW(area, targetY, targetX, width, height);
- break;
- case ComponentOrder.XYZ:
- this.CopyFromXYZ(area, targetY, targetX, width, height);
- break;
- case ComponentOrder.XYZW:
- this.CopyFromXYZW(area, targetY, targetX, width, height);
- break;
- default:
- throw new NotSupportedException();
- }
+ this.CopyTo(area, sourceX, sourceY, area.Width, area.Height);
}
///
- /// Copied an area of pixels to the image.
+ /// Copy pixels from the image to an area of pixels. This method will make sure that the pixels
+ /// that are copied are within the bounds of the image.
///
/// The area.
/// The source row index.
@@ -215,29 +186,21 @@ namespace ImageSharp
///
/// Thrown when an unsupported component order value is passed.
///
- public void CopyTo(PixelArea area, int sourceY, int sourceX = 0)
+ public void SafeCopyTo(PixelArea area, int sourceY, int sourceX = 0)
{
int width = Math.Min(area.Width, this.Width - sourceX);
- int height = Math.Min(area.Height, this.Height - sourceY);
+ if (width < 1)
+ {
+ return;
+ }
- this.CheckDimensions(width, height);
- switch (area.ComponentOrder)
+ int height = Math.Min(area.Height, this.Height - sourceY);
+ if (height < 1)
{
- case ComponentOrder.ZYX:
- this.CopyToZYX(area, sourceY, sourceX, width, height);
- break;
- case ComponentOrder.ZYXW:
- this.CopyToZYXW(area, sourceY, sourceX, width, height);
- break;
- case ComponentOrder.XYZ:
- this.CopyToXYZ(area, sourceY, sourceX, width, height);
- break;
- case ComponentOrder.XYZW:
- this.CopyToXYZW(area, sourceY, sourceX, width, height);
- break;
- default:
- throw new NotSupportedException();
+ return;
}
+
+ this.CopyTo(area, sourceX, sourceY, width, height);
}
///
@@ -269,6 +232,17 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
+ ///
+ /// Copies the pixels to another of the same size.
+ ///
+ /// The target pixel buffer accessor.
+ internal void CopyTo(PixelAccessor target)
+ {
+ uint byteCount = (uint)(this.Width * this.Height * Unsafe.SizeOf());
+
+ Unsafe.CopyBlock(target.pixelsBase, this.pixelsBase, byteCount);
+ }
+
///
/// Resets all the pixels to it's initial value.
///
@@ -278,21 +252,21 @@ namespace ImageSharp
}
///
- /// Copies from an area in format.
+ /// Copies from an area in format.
///
/// The area.
- /// The target row index.
/// The target column index.
+ /// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromZYX(PixelArea area, int targetY, int targetX, int width, int height)
+ protected virtual void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf();
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -307,21 +281,21 @@ namespace ImageSharp
}
///
- /// Copies from an area in format.
+ /// Copies from an area in format.
///
/// The area.
- /// The target row index.
/// The target column index.
+ /// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromZYXW(PixelArea area, int targetY, int targetX, int width, int height)
+ protected virtual void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf();
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -336,21 +310,21 @@ namespace ImageSharp
}
///
- /// Copies from an area in format.
+ /// Copies from an area in format.
///
/// The area.
- /// The target row index.
/// The target column index.
+ /// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromXYZ(PixelArea area, int targetY, int targetX, int width, int height)
+ protected virtual void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf();
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -365,21 +339,21 @@ namespace ImageSharp
}
///
- /// Copies from an area in format.
+ /// Copies from an area in format.
///
/// The area.
- /// The target row index.
/// The target column index.
+ /// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromXYZW(PixelArea area, int targetY, int targetX, int width, int height)
+ protected virtual void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf();
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -394,84 +368,84 @@ namespace ImageSharp
}
///
- /// Copies to an area in format.
+ /// Copies to an area in format.
///
/// The row.
- /// The source row index.
/// The source column index.
+ /// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToZYX(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected virtual void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowByteCount;
+ int offset = y * area.RowStride;
for (int x = 0; x < width; x++)
{
- this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.ZYX);
+ this[sourceX + x, sourceY + y].ToZyxBytes(area.Bytes, offset);
offset += 3;
}
}
}
///
- /// Copies to an area in format.
+ /// Copies to an area in format.
///
/// The row.
- /// The source row index.
/// The source column index.
+ /// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToZYXW(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected virtual void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowByteCount;
+ int offset = y * area.RowStride;
for (int x = 0; x < width; x++)
{
- this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.ZYXW);
+ this[sourceX + x, sourceY + y].ToZyxwBytes(area.Bytes, offset);
offset += 4;
}
}
}
///
- /// Copies to an area in format.
+ /// Copies to an area in format.
///
/// The row.
- /// The source row index.
/// The source column index.
+ /// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToXYZ(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected virtual void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowByteCount;
+ int offset = y * area.RowStride;
for (int x = 0; x < width; x++)
{
- this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.XYZ);
+ this[sourceX + x, sourceY + y].ToXyzBytes(area.Bytes, offset);
offset += 3;
}
}
}
///
- /// Copies to an area in format.
+ /// Copies to an area in format.
///
/// The row.
- /// The source row index.
/// The source column index.
+ /// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToXYZW(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected virtual void CopyToXyzw(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowByteCount;
+ int offset = y * area.RowStride;
for (int x = 0; x < width; x++)
{
- this[sourceX + x, sourceY + y].ToBytes(area.Bytes, offset, ComponentOrder.XYZW);
+ this[sourceX + x, sourceY + y].ToXyzwBytes(area.Bytes, offset);
offset += 4;
}
}
@@ -491,21 +465,88 @@ namespace ImageSharp
}
///
- /// Checks that the given dimensions are within the bounds of the image.
+ /// Copy an area of pixels to the image.
///
- /// The width.
- /// The height.
+ /// The area.
+ /// The target column index.
+ /// The target row index.
+ /// The width of the area to copy.
+ /// The height of the area to copy.
+ ///
+ /// Thrown when an unsupported component order value is passed.
+ ///
+ private void CopyFrom(PixelArea area, int targetX, int targetY, int width, int height)
+ {
+ switch (area.ComponentOrder)
+ {
+ case ComponentOrder.Zyx:
+ this.CopyFromZyx(area, targetX, targetY, width, height);
+ break;
+ case ComponentOrder.Zyxw:
+ this.CopyFromZyxw(area, targetX, targetY, width, height);
+ break;
+ case ComponentOrder.Xyz:
+ this.CopyFromXyz(area, targetX, targetY, width, height);
+ break;
+ case ComponentOrder.Xyzw:
+ this.CopyFromXyzw(area, targetX, targetY, width, height);
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ ///
+ /// Copy pixels from the image to an area of pixels.
+ ///
+ /// The area.
+ /// The source column index.
+ /// The source row index.
+ /// The width of the area to copy.
+ /// The height of the area to copy.
+ ///
+ /// Thrown when an unsupported component order value is passed.
+ ///
+ private void CopyTo(PixelArea area, int sourceX, int sourceY, int width, int height)
+ {
+ switch (area.ComponentOrder)
+ {
+ case ComponentOrder.Zyx:
+ this.CopyToZyx(area, sourceX, sourceY, width, height);
+ break;
+ case ComponentOrder.Zyxw:
+ this.CopyToZyxw(area, sourceX, sourceY, width, height);
+ break;
+ case ComponentOrder.Xyz:
+ this.CopyToXyz(area, sourceX, sourceY, width, height);
+ break;
+ case ComponentOrder.Xyzw:
+ this.CopyToXyzw(area, sourceX, sourceY, width, height);
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ ///
+ /// Checks that the given area and offset are within the bounds of the image.
+ ///
+ /// The area.
+ /// The x-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel.
+ /// The y-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel.
///
/// Thrown if the dimensions are not within the bounds of the image.
///
[Conditional("DEBUG")]
- private void CheckDimensions(int width, int height)
+ private void CheckCoordinates(PixelArea area, int x, int y)
{
+ int width = Math.Min(area.Width, this.Width - x);
if (width < 1)
{
throw new ArgumentOutOfRangeException(nameof(width), width, $"Invalid area size specified.");
}
+ int height = Math.Min(area.Height, this.Height - y);
if (height < 1)
{
throw new ArgumentOutOfRangeException(nameof(height), height, $"Invalid area size specified.");
diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs
index b1ae5f870..40da75c82 100644
--- a/src/ImageSharp/Image/PixelArea{TColor}.cs
+++ b/src/ImageSharp/Image/PixelArea{TColor}.cs
@@ -75,8 +75,10 @@ namespace ImageSharp
this.Width = width;
this.Height = height;
this.ComponentOrder = componentOrder;
- this.RowByteCount = width * GetComponentCount(componentOrder);
+ this.RowStride = width * GetComponentCount(componentOrder);
this.Bytes = bytes;
+ this.Length = bytes.Length;
+ this.isBufferRented = false;
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned);
// TODO: Why is Resharper warning us about an impure method call?
@@ -88,34 +90,31 @@ namespace ImageSharp
/// Initializes a new instance of the class.
///
/// The width.
- /// The height.
/// The component order.
- /// True if the buffer should be rented from ArrayPool
- public PixelArea(int width, int height, ComponentOrder componentOrder, bool usePool = false)
- : this(width, height, componentOrder, 0, usePool)
+ public PixelArea(int width, ComponentOrder componentOrder)
+ : this(width, 1, componentOrder, 0)
{
}
///
/// Initializes a new instance of the class.
///
- /// The width.
+ /// The width.
/// The component order.
- /// True if the buffer should be rented from ArrayPool
- public PixelArea(int width, ComponentOrder componentOrder, bool usePool = false)
- : this(width, 1, componentOrder, 0, usePool)
+ /// The number of bytes to pad each row.
+ public PixelArea(int width, ComponentOrder componentOrder, int padding)
+ : this(width, 1, componentOrder, padding)
{
}
///
/// Initializes a new instance of the class.
///
- /// The width.
+ /// The width.
+ /// The height.
/// The component order.
- /// The number of bytes to pad each row.
- /// True if the buffer should be rented from ArrayPool
- public PixelArea(int width, ComponentOrder componentOrder, int padding, bool usePool = false)
- : this(width, 1, componentOrder, padding, usePool)
+ public PixelArea(int width, int height, ComponentOrder componentOrder)
+ : this(width, height, componentOrder, 0)
{
}
@@ -126,27 +125,15 @@ namespace ImageSharp
/// The height.
/// The component order.
/// The number of bytes to pad each row.
- /// True if the buffer should be rented from ArrayPool
- public PixelArea(int width, int height, ComponentOrder componentOrder, int padding, bool usePool = false)
+ public PixelArea(int width, int height, ComponentOrder componentOrder, int padding)
{
this.Width = width;
this.Height = height;
this.ComponentOrder = componentOrder;
- this.RowByteCount = (width * GetComponentCount(componentOrder)) + padding;
-
- int bufferSize = this.RowByteCount * height;
-
- if (usePool)
- {
- this.Bytes = BytesPool.Rent(bufferSize);
- this.isBufferRented = true;
- Array.Clear(this.Bytes, 0, bufferSize);
- }
- else
- {
- this.Bytes = new byte[bufferSize];
- }
-
+ this.RowStride = (width * GetComponentCount(componentOrder)) + padding;
+ this.Length = this.RowStride * height;
+ this.Bytes = BytesPool.Rent(this.Length);
+ this.isBufferRented = true;
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned);
// TODO: Why is Resharper warning us about an impure method call?
@@ -167,6 +154,11 @@ namespace ImageSharp
///
public byte[] Bytes { get; }
+ ///
+ /// Gets the length of the buffer.
+ ///
+ public int Length { get; }
+
///
/// Gets the component order.
///
@@ -188,9 +180,9 @@ namespace ImageSharp
public byte* PixelBase { get; private set; }
///
- /// Gets number of bytes in a row.
+ /// Gets the width of one row in the number of bytes.
///
- public int RowByteCount { get; }
+ public int RowStride { get; }
///
/// Gets the width.
@@ -198,9 +190,9 @@ namespace ImageSharp
public int Width { get; }
///
- /// Gets the pool used to rent , when it's not coming from an external source
+ /// Gets the pool used to rent bytes, when it's not coming from an external source.
///
- // ReSharper disable once StaticMemberInGenericType
+ // TODO: Use own pool?
private static ArrayPool BytesPool => ArrayPool.Shared;
///
@@ -209,6 +201,13 @@ namespace ImageSharp
public void Dispose()
{
this.Dispose(true);
+
+ // This object will be cleaned up by the Dispose method.
+ // Therefore, you should call GC.SuppressFinalize to
+ // take this object off the finalization queue
+ // and prevent finalization code for this object
+ // from executing a second time.
+ GC.SuppressFinalize(this);
}
///
@@ -217,7 +216,7 @@ namespace ImageSharp
/// The stream.
public void Read(Stream stream)
{
- stream.Read(this.Bytes, 0, this.Bytes.Length);
+ stream.Read(this.Bytes, 0, this.Length);
}
///
@@ -226,7 +225,7 @@ namespace ImageSharp
/// The stream.
public void Write(Stream stream)
{
- stream.Write(this.Bytes, 0, this.Bytes.Length);
+ stream.Write(this.Bytes, 0, this.Length);
}
///
@@ -234,7 +233,7 @@ namespace ImageSharp
///
internal void Reset()
{
- Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowByteCount * this.Height));
+ Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowStride * this.Height));
}
///
@@ -251,17 +250,27 @@ namespace ImageSharp
{
switch (componentOrder)
{
- case ComponentOrder.ZYX:
- case ComponentOrder.XYZ:
+ case ComponentOrder.Zyx:
+ case ComponentOrder.Xyz:
return 3;
- case ComponentOrder.ZYXW:
- case ComponentOrder.XYZW:
+ case ComponentOrder.Zyxw:
+ case ComponentOrder.Xyzw:
return 4;
}
throw new NotSupportedException();
}
+ ///
+ /// Checks that the length of the byte array to ensure that it matches the given width and height.
+ ///
+ /// The width.
+ /// The height.
+ /// The byte array.
+ /// The component order.
+ ///
+ /// Thrown if the byte array is th incorrect length.
+ ///
[Conditional("DEBUG")]
private void CheckBytesLength(int width, int height, byte[] bytes, ComponentOrder componentOrder)
{
@@ -274,6 +283,10 @@ namespace ImageSharp
}
}
+ ///
+ /// Disposes the object and frees resources for the Garbage Collector.
+ ///
+ /// If true, the object gets disposed.
private void Dispose(bool disposing)
{
if (this.isDisposed)
@@ -300,13 +313,6 @@ namespace ImageSharp
this.PixelBase = null;
this.isDisposed = true;
-
- // This object will be cleaned up by the Dispose method.
- // Therefore, you should call GC.SuppressFinalize to
- // take this object off the finalization queue
- // and prevent finalization code for this object
- // from executing a second time.
- GC.SuppressFinalize(this);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelAccessor.cs b/src/ImageSharp/PixelAccessor.cs
index c91f10cbd..7827e7b47 100644
--- a/src/ImageSharp/PixelAccessor.cs
+++ b/src/ImageSharp/PixelAccessor.cs
@@ -22,13 +22,13 @@ namespace ImageSharp
}
///
- protected override void CopyFromXYZW(PixelArea area, int targetY, int targetX, int width, int height)
+ protected override void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height)
{
uint byteCount = (uint)width * 4;
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
Unsafe.CopyBlock(destination, source, byteCount);
@@ -36,11 +36,11 @@ namespace ImageSharp
}
///
- protected override void CopyFromXYZ(PixelArea area, int targetY, int targetX, int width, int height)
+ protected override void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -54,11 +54,11 @@ namespace ImageSharp
}
///
- protected override void CopyFromZYX(PixelArea area, int targetY, int targetX, int width, int height)
+ protected override void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -72,11 +72,11 @@ namespace ImageSharp
}
///
- protected override void CopyFromZYXW(PixelArea area, int targetY, int targetX, int width, int height)
+ protected override void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowByteCount);
+ byte* source = area.PixelBase + (y * area.RowStride);
byte* destination = this.GetRowPointer(targetX, targetY + y);
for (int x = 0; x < width; x++)
@@ -90,12 +90,12 @@ namespace ImageSharp
}
///
- protected override void CopyToZYX(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected override void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
byte* source = this.GetRowPointer(sourceX, sourceY + y);
- byte* destination = area.PixelBase + (y * area.RowByteCount);
+ byte* destination = area.PixelBase + (y * area.RowStride);
for (int x = 0; x < width; x++)
{
@@ -110,12 +110,12 @@ namespace ImageSharp
}
///
- protected override unsafe void CopyToXYZ(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected override void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
byte* source = this.GetRowPointer(sourceX, sourceY + y);
- byte* destination = area.PixelBase + (y * area.RowByteCount);
+ byte* destination = area.PixelBase + (y * area.RowStride);
for (int x = 0; x < width; x++)
{
@@ -130,12 +130,12 @@ namespace ImageSharp
}
///
- protected override void CopyToZYXW(PixelArea area, int sourceY, int sourceX, int width, int height)
+ protected override void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
byte* source = this.GetRowPointer(sourceX, sourceY + y);
- byte* destination = area.PixelBase + (y * area.RowByteCount);
+ byte* destination = area.PixelBase + (y * area.RowStride);
for (int x = 0; x < width; x++)
{
diff --git a/src/ImageSharp/Profiles/Exif/ExifTag.cs b/src/ImageSharp/Profiles/Exif/ExifTag.cs
index 894f5a064..6f4b49485 100644
--- a/src/ImageSharp/Profiles/Exif/ExifTag.cs
+++ b/src/ImageSharp/Profiles/Exif/ExifTag.cs
@@ -29,15 +29,15 @@ namespace ImageSharp
///
/// SubfileType
///
- [ExifTagDescription((uint)0, "Full-resolution Image")]
- [ExifTagDescription((uint)1, "Reduced-resolution image")]
- [ExifTagDescription((uint)2, "Single page of multi-page image")]
- [ExifTagDescription((uint)3, "Single page of multi-page reduced-resolution image")]
- [ExifTagDescription((uint)4, "Transparency mask")]
- [ExifTagDescription((uint)5, "Transparency mask of reduced-resolution image")]
- [ExifTagDescription((uint)6, "Transparency mask of multi-page image")]
- [ExifTagDescription((uint)7, "Transparency mask of reduced-resolution multi-page image")]
- [ExifTagDescription((uint)0x10001, "Alternate reduced-resolution image ")]
+ [ExifTagDescription(0U, "Full-resolution Image")]
+ [ExifTagDescription(1U, "Reduced-resolution image")]
+ [ExifTagDescription(2U, "Single page of multi-page image")]
+ [ExifTagDescription(3U, "Single page of multi-page reduced-resolution image")]
+ [ExifTagDescription(4U, "Transparency mask")]
+ [ExifTagDescription(5U, "Transparency mask of reduced-resolution image")]
+ [ExifTagDescription(6U, "Transparency mask of multi-page image")]
+ [ExifTagDescription(7U, "Transparency mask of reduced-resolution multi-page image")]
+ [ExifTagDescription(0x10001U, "Alternate reduced-resolution image ")]
SubfileType = 0x00FE,
///
@@ -276,15 +276,15 @@ namespace ImageSharp
///
/// T4Options
///
- [ExifTagDescription((uint)0, "2-Dimensional encoding")]
- [ExifTagDescription((uint)1, "Uncompressed")]
- [ExifTagDescription((uint)2, "Fill bits added")]
+ [ExifTagDescription(0U, "2-Dimensional encoding")]
+ [ExifTagDescription(1U, "Uncompressed")]
+ [ExifTagDescription(2U, "Fill bits added")]
T4Options = 0x0124,
///
/// T6Options
///
- [ExifTagDescription((uint)1, "Uncompressed")]
+ [ExifTagDescription(1U, "Uncompressed")]
T6Options = 0x0125,
///
@@ -383,9 +383,9 @@ namespace ImageSharp
///
/// CleanFaxData
///
- [ExifTagDescription((uint)0, "Clean")]
- [ExifTagDescription((uint)1, "Regenerated")]
- [ExifTagDescription((uint)2, "Unclean")]
+ [ExifTagDescription(0U, "Clean")]
+ [ExifTagDescription(1U, "Regenerated")]
+ [ExifTagDescription(2U, "Unclean")]
CleanFaxData = 0x0147,
///
@@ -491,8 +491,8 @@ namespace ImageSharp
///
/// ProfileType
///
- [ExifTagDescription((uint)0, "Unspecified")]
- [ExifTagDescription((uint)1, "Group 3 FAX")]
+ [ExifTagDescription(0U, "Unspecified")]
+ [ExifTagDescription(1U, "Group 3 FAX")]
ProfileType = 0x0191,
///
@@ -512,13 +512,13 @@ namespace ImageSharp
///
/// CodingMethods
///
- [ExifTagDescription((ulong)0, "Unspecified compression")]
- [ExifTagDescription((ulong)1, "Modified Huffman")]
- [ExifTagDescription((ulong)2, "Modified Read")]
- [ExifTagDescription((ulong)4, "Modified MR")]
- [ExifTagDescription((ulong)8, "JBIG")]
- [ExifTagDescription((ulong)16, "Baseline JPEG")]
- [ExifTagDescription((ulong)32, "JBIG color")]
+ [ExifTagDescription(0UL, "Unspecified compression")]
+ [ExifTagDescription(1UL, "Modified Huffman")]
+ [ExifTagDescription(2UL, "Modified Read")]
+ [ExifTagDescription(4UL, "Modified MR")]
+ [ExifTagDescription(8UL, "JBIG")]
+ [ExifTagDescription(16UL, "Baseline JPEG")]
+ [ExifTagDescription(32UL, "JBIG color")]
CodingMethods = 0x0193,
///
diff --git a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs
index 2898a5b71..2cd2de4f5 100644
--- a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs
+++ b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs
@@ -47,12 +47,7 @@ namespace ImageSharp.Quantizers
public override QuantizedImage Quantize(ImageBase image, int maxColors)
{
this.colors = maxColors.Clamp(1, 255);
-
- if (this.octree == null)
- {
- // Construct the Octree
- this.octree = new Octree(this.GetBitsNeededForColorDepth(maxColors));
- }
+ this.octree = new Octree(this.GetBitsNeededForColorDepth(maxColors));
return base.Quantize(image, maxColors);
}
@@ -117,6 +112,7 @@ namespace ImageSharp.Quantizers
///
/// Mask used when getting the appropriate pixels for a given node
///
+ // ReSharper disable once StaticMemberInGenericType
private static readonly int[] Mask = { 0x100, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
///
@@ -380,7 +376,7 @@ namespace ImageSharp.Quantizers
{
// Go to the next level down in the tree
int shift = 7 - level;
- pixel.ToBytes(buffer, 0, ComponentOrder.XYZW);
+ pixel.ToXyzwBytes(buffer, 0);
int index = ((buffer[3] & Mask[0]) >> (shift - 3)) |
((buffer[2] & Mask[level + 1]) >> (shift - 2)) |
@@ -484,7 +480,7 @@ namespace ImageSharp.Quantizers
if (!this.leaf)
{
int shift = 7 - level;
- pixel.ToBytes(buffer, 0, ComponentOrder.XYZW);
+ pixel.ToXyzwBytes(buffer, 0);
int pixelIndex = ((buffer[3] & Mask[0]) >> (shift - 3)) |
((buffer[2] & Mask[level + 1]) >> (shift - 2)) |
@@ -511,7 +507,7 @@ namespace ImageSharp.Quantizers
/// The buffer array.
public void Increment(TColor pixel, byte[] buffer)
{
- pixel.ToBytes(buffer, 0, ComponentOrder.XYZW);
+ pixel.ToXyzwBytes(buffer, 0);
this.pixelCount++;
this.red += buffer[0];
this.green += buffer[1];
diff --git a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs
index 92e0ae3c6..6edb7801b 100644
--- a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs
+++ b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs
@@ -49,7 +49,7 @@ namespace ImageSharp.Quantizers
for (int i = 0; i < constants.Length; i++)
{
- constants[i].ToBytes(this.pixelBuffer, 0, ComponentOrder.XYZW);
+ constants[i].ToXyzwBytes(this.pixelBuffer, 0);
TColor packed = default(TColor);
packed.PackFromBytes(this.pixelBuffer[0], this.pixelBuffer[1], this.pixelBuffer[2], this.pixelBuffer[3]);
safe[i] = packed;
diff --git a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs b/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs
index 2b491ff4c..e235e68e8 100644
--- a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs
+++ b/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs
@@ -33,11 +33,6 @@ namespace ImageSharp.Quantizers
public sealed class WuQuantizer : IQuantizer
where TColor : struct, IPackedPixel, IEquatable
{
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 1e-5F;
-
///
/// The index bits.
///
@@ -340,7 +335,7 @@ namespace ImageSharp.Quantizers
for (int x = 0; x < pixels.Width; x++)
{
// Colors are expected in r->g->b->a format
- pixels[x, y].ToBytes(this.rgbaBuffer, 0, ComponentOrder.XYZW);
+ pixels[x, y].ToXyzwBytes(this.rgbaBuffer, 0);
byte r = this.rgbaBuffer[0];
byte g = this.rgbaBuffer[1];
@@ -542,7 +537,7 @@ namespace ImageSharp.Quantizers
double temp;
- if (Math.Abs(halfW) < Epsilon)
+ if (Math.Abs(halfW) < Constants.Epsilon)
{
continue;
}
@@ -555,7 +550,7 @@ namespace ImageSharp.Quantizers
halfA = wholeA - halfA;
halfW = wholeW - halfW;
- if (Math.Abs(halfW) < Epsilon)
+ if (Math.Abs(halfW) < Constants.Epsilon)
{
continue;
}
@@ -762,7 +757,7 @@ namespace ImageSharp.Quantizers
double weight = Volume(cube[k], this.vwt);
- if (Math.Abs(weight) > Epsilon)
+ if (Math.Abs(weight) > Constants.Epsilon)
{
float r = (float)(Volume(cube[k], this.vmr) / weight);
float g = (float)(Volume(cube[k], this.vmg) / weight);
@@ -785,7 +780,7 @@ namespace ImageSharp.Quantizers
for (int x = 0; x < width; x++)
{
// Expected order r->g->b->a
- imagePixels[x, y].ToBytes(rgba, 0, ComponentOrder.XYZW);
+ imagePixels[x, y].ToXyzwBytes(rgba, 0);
int r = rgba[0] >> (8 - IndexBits);
int g = rgba[1] >> (8 - IndexBits);
diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json
index 33272594d..91cd27714 100644
--- a/src/ImageSharp/project.json
+++ b/src/ImageSharp/project.json
@@ -24,7 +24,7 @@
"buildOptions": {
"allowUnsafe": true,
"xmlDoc": true,
- "additionalArguments": [ "/additionalfile:stylecop.json" ]
+ "additionalArguments": [ "/additionalfile:stylecop.json", "/ruleset:../../ImageSharp.ruleset" ]
},
"configurations": {
"Release": {
@@ -36,7 +36,7 @@
},
"dependencies": {
"StyleCop.Analyzers": {
- "version": "1.0.0",
+ "version": "1.1.0-beta001",
"type": "build"
},
"System.Buffers": "4.0.0",
diff --git a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs
index 1003aeaeb..3c2b1b5d2 100644
--- a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs
+++ b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs
@@ -37,26 +37,5 @@ namespace ImageSharp.Benchmarks.Image
return targetPixels[0, 0];
}
}
-
- [Benchmark(Description = "Copy by Row")]
- public CoreColor CopyByRow()
- {
- CoreImage source = new CoreImage(1024, 768);
- CoreImage target = new CoreImage(1024, 768);
- using (PixelAccessor sourcePixels = source.Lock())
- using (PixelAccessor targetPixels = target.Lock())
- {
- Parallel.For(
- 0,
- source.Height,
- Bootstrapper.ParallelOptions,
- y =>
- {
- sourcePixels.CopyBlock(0, y, targetPixels, 0, y, source.Width);
- });
-
- return targetPixels[0, 0];
- }
- }
}
}
diff --git a/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs b/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs
index f1b6d5f9b..488455752 100644
--- a/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs
+++ b/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs
@@ -7,7 +7,7 @@ namespace ImageSharp.Tests
{
using System;
using System.Diagnostics.CodeAnalysis;
-
+ using ImageSharp.Colors.Spaces;
using Xunit;
///
diff --git a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs
index 77fee1e79..a79ef620e 100644
--- a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs
+++ b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs
@@ -15,7 +15,7 @@ namespace ImageSharp.Tests.Colors
///
///
/// The "ToVector4" tests should now be covered in
- /// and at some point they can be safely removed from here.
+ /// and at some point they can be safely removed from here.
///
public class PackedPixelTests
{
@@ -47,16 +47,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Alpha8(.5F).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Alpha8(.5F).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 0, 0, 0 });
- new Alpha8(.5F).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Alpha8(.5F).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 0, 0, 0, 128 });
- new Alpha8(.5F).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Alpha8(.5F).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 0, 0 });
- new Alpha8(.5F).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Alpha8(.5F).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 0, 0, 128 });
}
@@ -92,16 +92,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- argb.ToBytes(rgb, 0, ComponentOrder.XYZ);
+ argb.ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 });
- argb.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ argb.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 });
- argb.ToBytes(bgr, 0, ComponentOrder.ZYX);
+ argb.ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a });
- argb.ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ argb.ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 });
}
@@ -133,23 +133,22 @@ namespace ImageSharp.Tests.Colors
float z = 0.5F;
Assert.Equal(6160, new Bgr565(x, y, z).PackedValue);
-
// Test ordering
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Bgr565(x, y, z).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Bgr565(x, y, z).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 25, 0, 132 });
- new Bgr565(x, y, z).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Bgr565(x, y, z).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 25, 0, 132, 255 });
- new Bgr565(x, y, z).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Bgr565(x, y, z).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 132, 0, 25 });
- new Bgr565(x, y, z).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Bgr565(x, y, z).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 132, 0, 25, 255 });
}
@@ -190,16 +189,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Bgra4444(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Bgra4444(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 34, 0, 136 });
- new Bgra4444(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Bgra4444(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 34, 0, 136, 0 });
- new Bgra4444(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Bgra4444(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 136, 0, 34 });
- new Bgra4444(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Bgra4444(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 136, 0, 34, 0 });
}
@@ -236,16 +235,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Bgra5551(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Bgra5551(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 24, 0, 131 });
- new Bgra5551(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Bgra5551(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 24, 0, 131, 0 });
- new Bgra5551(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Bgra5551(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 131, 0, 24 });
- new Bgra5551(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Bgra5551(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 131, 0, 24, 0 });
}
@@ -287,21 +286,21 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Byte4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Byte4(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 128, 0, 0 });
- new Byte4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Byte4(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 128, 0, 0, 0 });
- new Byte4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Byte4(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 0, 128 });
- new Byte4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Byte4(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 0, 128, 0 });
Byte4 r = new Byte4();
r.PackFromBytes(20, 38, 0, 255);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 });
}
@@ -326,16 +325,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new HalfSingle(x).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new HalfSingle(x).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 128, 0, 0 });
- new HalfSingle(x).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new HalfSingle(x).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 128, 0, 0, 255 });
- new HalfSingle(x).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new HalfSingle(x).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 0, 128 });
- new HalfSingle(x).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new HalfSingle(x).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 0, 128, 255 });
}
@@ -363,16 +362,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new HalfVector2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new HalfVector2(x, y).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 128, 64, 0 });
- new HalfVector2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new HalfVector2(x, y).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 128, 64, 0, 255 });
- new HalfVector2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new HalfVector2(x, y).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 64, 128 });
- new HalfVector2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new HalfVector2(x, y).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 64, 128, 255 });
}
@@ -409,16 +408,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new HalfVector4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new HalfVector4(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 64, 128, 191 });
- new HalfVector4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new HalfVector4(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 64, 128, 191, 255 });
- new HalfVector4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new HalfVector4(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 191, 128, 64 });
- new HalfVector4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new HalfVector4(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 191, 128, 64, 255 });
}
@@ -454,16 +453,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new NormalizedByte2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new NormalizedByte2(x, y).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 141, 90, 0 });
- new NormalizedByte2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new NormalizedByte2(x, y).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 141, 90, 0, 255 });
- new NormalizedByte2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new NormalizedByte2(x, y).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 90, 141 });
- new NormalizedByte2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new NormalizedByte2(x, y).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 90, 141, 255 });
}
@@ -499,26 +498,26 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new NormalizedByte4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new NormalizedByte4(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 141, 90, 192 });
- new NormalizedByte4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new NormalizedByte4(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 141, 90, 192, 39 });
- new NormalizedByte4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new NormalizedByte4(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 192, 90, 141 });
- new NormalizedByte4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new NormalizedByte4(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 });
// http://community.monogame.net/t/normalizedbyte4-texture2d-gives-different-results-from-xna/8012/8
NormalizedByte4 r = new NormalizedByte4();
r.PackFromBytes(9, 115, 202, 127);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 });
r.PackedValue = 0xff4af389;
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 });
}
@@ -553,22 +552,22 @@ namespace ImageSharp.Tests.Colors
NormalizedShort2 n = new NormalizedShort2();
n.PackFromBytes(141, 90, 0, 0);
- n.ToBytes(rgb, 0, ComponentOrder.XYZ);
+ n.ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 141, 90, 0 });
// TODO: I don't think this can ever pass since the bytes are already truncated.
// Assert.Equal(3650751693, n.PackedValue);
- new NormalizedShort2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new NormalizedShort2(x, y).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 141, 90, 0 });
- new NormalizedShort2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new NormalizedShort2(x, y).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 141, 90, 0, 255 });
- new NormalizedShort2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new NormalizedShort2(x, y).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 90, 141 });
- new NormalizedShort2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new NormalizedShort2(x, y).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 90, 141, 255 });
}
@@ -600,21 +599,21 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new NormalizedShort4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new NormalizedShort4(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 141, 90, 192 });
- new NormalizedShort4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new NormalizedShort4(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 141, 90, 192, 39 });
- new NormalizedShort4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new NormalizedShort4(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 192, 90, 141 });
- new NormalizedShort4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new NormalizedShort4(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 });
NormalizedShort4 r = new NormalizedShort4();
r.PackFromBytes(9, 115, 202, 127);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 });
}
@@ -647,16 +646,16 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Rg32(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Rg32(x, y).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 25, 0, 0 });
- new Rg32(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Rg32(x, y).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 25, 0, 0, 255 });
- new Rg32(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Rg32(x, y).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 0, 25 });
- new Rg32(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Rg32(x, y).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 0, 25, 255 });
}
@@ -692,22 +691,22 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Rgba1010102(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Rgba1010102(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 25, 0, 128 });
- new Rgba1010102(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Rgba1010102(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 });
- new Rgba1010102(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Rgba1010102(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 128, 0, 25 });
- new Rgba1010102(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Rgba1010102(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 128, 0, 25, 0 });
// Alpha component accuracy will be awful.
Rgba1010102 r = new Rgba1010102();
r.PackFromBytes(25, 0, 128, 0);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 });
}
@@ -741,21 +740,21 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Rgba64(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Rgba64(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 20, 38, 76 });
- new Rgba64(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Rgba64(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 });
- new Rgba64(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Rgba64(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 76, 38, 20 });
- new Rgba64(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Rgba64(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 76, 38, 20, 115 });
Rgba64 r = new Rgba64();
r.PackFromBytes(20, 38, 76, 115);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 });
}
@@ -796,21 +795,21 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Short2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Short2(x, y).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 128, 127, 0 });
- new Short2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Short2(x, y).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 128, 127, 0, 255 });
- new Short2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Short2(x, y).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 0, 127, 128 });
- new Short2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Short2(x, y).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 0, 127, 128, 255 });
Short2 r = new Short2();
r.PackFromBytes(20, 38, 0, 255);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 });
}
@@ -853,21 +852,21 @@ namespace ImageSharp.Tests.Colors
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
- new Short4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
+ new Short4(x, y, z, w).ToXyzBytes(rgb, 0);
Assert.Equal(rgb, new byte[] { 172, 177, 243 });
- new Short4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
+ new Short4(x, y, z, w).ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 172, 177, 243, 128 });
- new Short4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
+ new Short4(x, y, z, w).ToZyxBytes(bgr, 0);
Assert.Equal(bgr, new byte[] { 243, 177, 172 });
- new Short4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
+ new Short4(x, y, z, w).ToZyxwBytes(bgra, 0);
Assert.Equal(bgra, new byte[] { 243, 177, 172, 128 });
Short4 r = new Short4();
r.PackFromBytes(20, 38, 0, 255);
- r.ToBytes(rgba, 0, ComponentOrder.XYZW);
+ r.ToXyzwBytes(rgba, 0);
Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 });
}
diff --git a/tests/ImageSharp.Tests/Common/ConstantsTests.cs b/tests/ImageSharp.Tests/Common/ConstantsTests.cs
new file mode 100644
index 000000000..0adda0c0f
--- /dev/null
+++ b/tests/ImageSharp.Tests/Common/ConstantsTests.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests.Common
+{
+ using Xunit;
+
+ public class ConstantsTests
+ {
+ [Fact]
+ public void Epsilon()
+ {
+ Assert.Equal(Constants.Epsilon, 0.001f);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 57b3c56f8..9bae8d9f3 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -428,5 +428,21 @@ namespace ImageSharp.Tests
Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f));
}
+
+ [Fact]
+ public unsafe void Copy_FromHeap()
+ {
+ Block8x8F[] blox = new Block8x8F[1];
+
+ blox[0].LoadFrom(Create8x8FloatData());
+ Block8x8F clone = default(Block8x8F);
+
+ fixed (Block8x8F* p = &blox[0])
+ {
+ Block8x8F.Copy(&clone, p);
+ }
+
+ Assert.Equal(blox[0], clone);
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
index 5168d1d67..46847873a 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
@@ -83,7 +83,7 @@ namespace ImageSharp.Tests
byte[] bytes = File.ReadAllBytes(path);
this.Measure(
- 40,
+ 100,
() =>
{
Image img = new Image(bytes);
@@ -146,7 +146,7 @@ namespace ImageSharp.Tests
{
var src = provider.GetImage();
- PixelArea area = new PixelArea(8, 8, ComponentOrder.XYZ);
+ PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz);
var dest = provider.Factory.CreateImage(8, 8);
using (var s = src.Lock())
@@ -171,7 +171,7 @@ namespace ImageSharp.Tests
{
var src = provider.GetImage();
- PixelArea area = new PixelArea(8, 8, ComponentOrder.XYZ);
+ PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz);
var dest = provider.Factory.CreateImage(8, 8);
using (var s = src.Lock())
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
index e7bfdf06b..d3f29eb25 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
@@ -8,7 +8,7 @@ namespace ImageSharp.Tests
using ImageSharp.Formats;
using ImageSharp.Formats.Jpg;
-
+
///
/// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests
/// Floating point DCT code Ported from https://github.com/norishigefukushima/dct_simd
@@ -365,11 +365,11 @@ namespace ImageSharp.Tests
float a0, a1, a2, a3, b0, b1, b2, b3;
float z0, z1, z2, z3, z4;
- float r0 = 1.414214f;
+ //float r0 = 1.414214f;
float r1 = 1.387040f;
float r2 = 1.306563f;
float r3 = 1.175876f;
- float r4 = 1.000000f;
+ //float r4 = 1.000000f;
float r5 = 0.785695f;
float r6 = 0.541196f;
float r7 = 0.275899f;
@@ -797,7 +797,7 @@ namespace ImageSharp.Tests
r[7] = 0.275899f;
const float invsqrt2 = 0.707107f; //(float)(1.0f / M_SQRT2);
- const float invsqrt2h = 0.353554f; //invsqrt2*0.5f;
+ //const float invsqrt2h = 0.353554f; //invsqrt2*0.5f;
c1 = x[0];
c2 = x[7];
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs b/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
index 74c6772b7..650b9fc07 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
@@ -15,7 +15,7 @@ namespace ImageSharp.Tests
///
/// Utility class to measure the execution of an operation.
///
- public class MeasureFixture
+ public class MeasureFixture : TestBase
{
protected bool EnablePrinting = true;
diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
index fbba55815..ce28518d7 100644
--- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
+++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
@@ -40,10 +40,10 @@ namespace ImageSharp.Tests
}
[Theory]
- [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.XYZ)]
- [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.ZYX)]
- [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.XYZW)]
- [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.ZYXW)]
+ [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyz)]
+ [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyx)]
+ [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)]
+ [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)]
public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable
{
@@ -84,10 +84,10 @@ namespace ImageSharp.Tests
}
[Theory]
- [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.XYZ)]
- [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.ZYX)]
- [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.XYZW)]
- [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.ZYXW)]
+ [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyz)]
+ [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)]
+ [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)]
+ [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)]
public void CopyTo_Then_CopyFrom_WithOffset(TestImageProvider provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable
@@ -181,7 +181,7 @@ namespace ImageSharp.Tests
byte blue = 3;
byte alpha = 255;
- using (PixelArea row = new PixelArea(1, ComponentOrder.ZYX))
+ using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx))
{
row.Bytes[0] = blue;
row.Bytes[1] = green;
@@ -208,7 +208,7 @@ namespace ImageSharp.Tests
byte blue = 3;
byte alpha = 4;
- using (PixelArea row = new PixelArea(1, ComponentOrder.ZYXW))
+ using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw))
{
row.Bytes[0] = blue;
row.Bytes[1] = green;
@@ -235,7 +235,7 @@ namespace ImageSharp.Tests
byte green = 2;
byte blue = 3;
- using (PixelArea row = new PixelArea(1, ComponentOrder.ZYX))
+ using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx))
{
pixels[0, 0] = (TColor)(object)new Color(red, green, blue);
@@ -258,7 +258,7 @@ namespace ImageSharp.Tests
byte blue = 3;
byte alpha = 4;
- using (PixelArea row = new PixelArea(1, ComponentOrder.ZYXW))
+ using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw))
{
pixels[0, 0] = (TColor)(object)new Color(red, green, blue, alpha);
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
index bb517dda9..8b0c65a3c 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
@@ -21,7 +21,7 @@ namespace ImageSharp.Tests
private static readonly Assembly ImageSharpAssembly = typeof(Color).GetTypeInfo().Assembly;
private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary();
-
+
private static readonly PixelTypes[] AllConcretePixelTypes = EnumHelper.GetSortedValues()
.Except(new [] {PixelTypes.Undefined, PixelTypes.All })
.ToArray();
@@ -53,7 +53,7 @@ namespace ImageSharp.Tests
return intrfcType.GetGenericArguments().Single();
}
-
+
public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag;
public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true)
@@ -87,11 +87,11 @@ namespace ImageSharp.Tests
}
else
{
- ca.ToBytes(bytesA, 0, ComponentOrder.XYZ);
- cb.ToBytes(bytesB, 0, ComponentOrder.XYZ);
+ ca.ToXyzBytes(bytesA, 0);
+ cb.ToXyzBytes(bytesB, 0);
- if (bytesA[0] != bytesB[0] ||
- bytesA[1] != bytesB[1] ||
+ if (bytesA[0] != bytesB[0] ||
+ bytesA[1] != bytesB[1] ||
bytesA[2] != bytesB[2])
{
return false;
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
index 31014f89d..2f5ec6c28 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
@@ -35,7 +35,7 @@ namespace ImageSharp.Tests
[WithBlankImages(42, 666, PixelTypes.All, "hello")]
public void Use_WithBlankImagesAttribute_WithAllPixelTypes(
TestImageProvider provider,
- string message)
+ string message)
where TColor : struct, IPackedPixel, IEquatable
{
var img = provider.GetImage();
@@ -59,7 +59,7 @@ namespace ImageSharp.Tests
[WithBlankImages(1, 1, PixelTypes.StandardImageClass)]
[WithFile(TestImages.Bmp.F, PixelTypes.StandardImageClass)]
public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass(
- TestImageProvider provider)
+ TestImageProvider provider)
where TColor : struct, IPackedPixel, IEquatable
{
var img = provider.GetImage();
@@ -112,7 +112,7 @@ namespace ImageSharp.Tests
{
for (int x = 0; x < pixels.Width; x++)
{
- pixels[x, y].ToBytes(colors, 0, ComponentOrder.XYZW);
+ pixels[x, y].ToXyzwBytes(colors, 0);
Assert.Equal(colors[0], 255);
Assert.Equal(colors[1], 100);
@@ -129,7 +129,7 @@ namespace ImageSharp.Tests
///
///
///
- public static Image CreateTestImage(GenericFactory factory)
+ public static Image CreateTestImage(GenericFactory factory)
where TColor : struct, IPackedPixel, IEquatable
{
return factory.CreateImage(3, 3);
diff --git a/tests/ImageSharp.Tests/project.json b/tests/ImageSharp.Tests/project.json
index cdf1aa61f..e0e269239 100644
--- a/tests/ImageSharp.Tests/project.json
+++ b/tests/ImageSharp.Tests/project.json
@@ -12,6 +12,13 @@
"buildOptions": {
"allowUnsafe": true
},
+ "configurations": {
+ "Release": {
+ "buildOptions": {
+ "warningsAsErrors": true
+ }
+ }
+ },
"dependencies": {
"ImageSharp": "1.0.0-*",
"xunit": "2.2.0-*",
diff --git a/tests/ImageSharp.Tests46/HelloTest.cs b/tests/ImageSharp.Tests46/HelloTest.cs
new file mode 100644
index 000000000..6a31d9bdd
--- /dev/null
+++ b/tests/ImageSharp.Tests46/HelloTest.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ImageSharp.Tests
+{
+ using Xunit;
+ using Xunit.Abstractions;
+
+ public class HelloTest
+ {
+ private ITestOutputHelper output;
+
+ public HelloTest(ITestOutputHelper output)
+ {
+ this.output = output;
+ }
+
+ [Fact]
+ public void HelloFoo()
+ {
+ TestFile file = TestFile.Create(TestImages.Jpeg.Calliphora);
+ var img = file.CreateImage();
+ this.output.WriteLine(img.Width.ToString());
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
new file mode 100644
index 000000000..d53ee3d9c
--- /dev/null
+++ b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
@@ -0,0 +1,179 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}
+ Library
+ Properties
+ ImageSharp.Tests
+ ImageSharp.Tests46
+ v4.6.1
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+ ..\..\src\ImageSharp\bin\Debug\net45\ImageSharp.dll
+
+
+
+
+ ..\..\src\ImageSharp\bin\Release\net45\ImageSharp.dll
+
+
+
+
+
+
+
+ ..\..\packages\System.Numerics.Vectors.4.1.1\lib\net46\System.Numerics.Vectors.dll
+ True
+
+
+
+
+
+
+
+
+ ..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll
+ True
+
+
+ ..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll
+ True
+
+
+ ..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll
+ True
+
+
+ ..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll
+ True
+
+
+
+
+ Formats\Jpg\Block8x8FTests.cs
+
+
+ Formats\Jpg\JpegTests.cs
+
+
+ Formats\Jpg\ReferenceImplementations.cs
+
+
+ Formats\Jpg\ReferenceImplementationsTests.cs
+
+
+ Formats\Jpg\UtilityTestClassBase.cs
+
+
+ Image\ImagePropertyTests.cs
+
+
+ Image\ImageTests.cs
+
+
+ Image\PixelAccessorTests.cs
+
+
+ TestBase.cs
+
+
+ TestImages.cs
+
+
+ TestUtilities\ImageDataAttributeBase.cs
+
+
+ TestUtilities\WithBlankImageAttribute.cs
+
+
+ TestUtilities\WithFileAttribute.cs
+
+
+ TestUtilities\WithFileCollectionAttribute.cs
+
+
+ TestUtilities\WithMemberFactoryAttribute.cs
+
+
+ TestUtilities\WithSolidFilledImagesAttribute.cs
+
+
+ TestUtilities\EnumHelper.cs
+
+
+ TestUtilities\GenericFactory.cs
+
+
+ TestUtilities\ImageFactory.cs
+
+
+ TestUtilities\BlankProvider.cs
+
+
+ TestUtilities\FileProvider.cs
+
+
+ TestUtilities\LambdaProvider.cs
+
+
+ TestUtilities\SolidProvider.cs
+
+
+ TestUtilities\TestImageProvider.cs
+
+
+ TestUtilities\ImagingTestCaseUtility.cs
+
+
+ TestUtilities\PixelTypes.cs
+
+
+ TestUtilities\TestImageProviderTests.cs
+
+
+ TestUtilities\TestUtilityExtensionsTests.cs
+
+
+ TestUtilities\TestUtilityExtensions.cs
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests46/Properties/AssemblyInfo.cs b/tests/ImageSharp.Tests46/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..d16593ab0
--- /dev/null
+++ b/tests/ImageSharp.Tests46/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ImageSharp.Tests46")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Sapa")]
+[assembly: AssemblyProduct("ImageSharp.Tests46")]
+[assembly: AssemblyCopyright("Copyright © Sapa 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("88c5fb74-5845-4cc0-8f1e-a25ebabc95c2")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tests/ImageSharp.Tests46/TestFile.cs b/tests/ImageSharp.Tests46/TestFile.cs
new file mode 100644
index 000000000..1b9842857
--- /dev/null
+++ b/tests/ImageSharp.Tests46/TestFile.cs
@@ -0,0 +1,78 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+namespace ImageSharp.Tests
+{
+ using System;
+ using System.Collections.Concurrent;
+ using System.IO;
+
+ public class TestFile
+ {
+ private static readonly ConcurrentDictionary cache = new ConcurrentDictionary();
+ private static readonly string FormatsDirectory = GetFormatsDirectory();
+
+ private static string GetFormatsDirectory()
+ {
+ return "../../../ImageSharp.Tests/TestImages/Formats/";
+ }
+
+ private readonly Image image;
+ private readonly string file;
+
+ private TestFile(string file)
+ {
+ this.file = file;
+
+ this.Bytes = File.ReadAllBytes(file);
+ this.image = new Image(this.Bytes);
+ }
+
+ public static string GetPath(string file)
+ {
+ return Path.Combine(FormatsDirectory, file);
+ }
+
+ public static TestFile Create(string file)
+ {
+ return cache.GetOrAdd(file, (string fileName) =>
+ {
+ return new TestFile(FormatsDirectory + fileName);
+ });
+ }
+
+ public byte[] Bytes { get; }
+
+ public string FileName
+ {
+ get
+ {
+ return Path.GetFileName(this.file);
+ }
+ }
+
+ public string FileNameWithoutExtension
+ {
+ get
+ {
+ return Path.GetFileNameWithoutExtension(this.file);
+ }
+ }
+
+ public string GetFileName(object value)
+ {
+ return this.FileNameWithoutExtension + "-" + value + Path.GetExtension(this.file);
+ }
+
+ public string GetFileNameWithoutExtension(object value)
+ {
+ return this.FileNameWithoutExtension + "-" + value;
+ }
+
+ public Image CreateImage()
+ {
+ return new Image(this.image);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests46/packages.config b/tests/ImageSharp.Tests46/packages.config
new file mode 100644
index 000000000..4eced5344
--- /dev/null
+++ b/tests/ImageSharp.Tests46/packages.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file