Browse Source

Merge branch 'jpeg-optimizations-experimental' of https://github.com/antonfirsov/ImageSharp into jpeg-optimizations

pull/62/head
Anton Firszov 9 years ago
parent
commit
bfd0196046
  1. 6
      ImageSharp.ruleset
  2. 10
      ImageSharp.sln
  3. 1
      ImageSharp.sln.DotSettings
  4. 3
      README.md
  5. 2
      global.json
  6. 58
      src/ImageSharp/Colors/Color.cs
  7. 20
      src/ImageSharp/Colors/ColorspaceTransforms.cs
  8. 8
      src/ImageSharp/Colors/ComponentOrder.cs
  9. 58
      src/ImageSharp/Colors/PackedPixel/Alpha8.cs
  10. 60
      src/ImageSharp/Colors/PackedPixel/Argb.cs
  11. 60
      src/ImageSharp/Colors/PackedPixel/Bgr565.cs
  12. 60
      src/ImageSharp/Colors/PackedPixel/Bgra4444.cs
  13. 60
      src/ImageSharp/Colors/PackedPixel/Bgra5551.cs
  14. 60
      src/ImageSharp/Colors/PackedPixel/Byte4.cs
  15. 73
      src/ImageSharp/Colors/PackedPixel/HalfSingle.cs
  16. 73
      src/ImageSharp/Colors/PackedPixel/HalfVector2.cs
  17. 73
      src/ImageSharp/Colors/PackedPixel/HalfVector4.cs
  18. 32
      src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs
  19. 79
      src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs
  20. 79
      src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs
  21. 79
      src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
  22. 79
      src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
  23. 64
      src/ImageSharp/Colors/PackedPixel/Rg32.cs
  24. 64
      src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
  25. 64
      src/ImageSharp/Colors/PackedPixel/Rgba64.cs
  26. 79
      src/ImageSharp/Colors/PackedPixel/Short2.cs
  27. 91
      src/ImageSharp/Colors/PackedPixel/Short4.cs
  28. 17
      src/ImageSharp/Colors/Spaces/Bgra32.cs
  29. 24
      src/ImageSharp/Colors/Spaces/CieLab.cs
  30. 24
      src/ImageSharp/Colors/Spaces/CieXyz.cs
  31. 26
      src/ImageSharp/Colors/Spaces/Cmyk.cs
  32. 32
      src/ImageSharp/Colors/Spaces/Hsl.cs
  33. 32
      src/ImageSharp/Colors/Spaces/Hsv.cs
  34. 2
      src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs
  35. 33
      src/ImageSharp/Colors/Spaces/YCbCr.cs
  36. 13
      src/ImageSharp/Colors/Vector4BlendTransforms.cs
  37. 18
      src/ImageSharp/Common/Constants.cs
  38. 17
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  39. 4
      src/ImageSharp/Drawing/Processors/DrawPathProcessor.cs
  40. 4
      src/ImageSharp/Drawing/Processors/FillShapeProcessor.cs
  41. 7
      src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs
  42. 13
      src/ImageSharp/Filters/Processors/Transforms/RotateProcessor.cs
  43. 6
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  44. 8
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  45. 2
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  46. 7
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  47. 7
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  48. 101
      src/ImageSharp/Formats/Jpg/Components/Decoder/GrayImage.cs
  49. 199
      src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs
  50. 132
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs
  51. 653
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.cs
  52. 25
      src/ImageSharp/Formats/Jpg/Components/Decoder/JpegScanDecoder.md
  53. 26
      src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs
  54. 160
      src/ImageSharp/Formats/Jpg/Components/Decoder/YCbCrImage.cs
  55. 2414
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  56. 21
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  57. 30
      src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs
  58. 37
      src/ImageSharp/Formats/Jpg/Utils/JpegUtils.cs
  59. 6
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  60. 2
      src/ImageSharp/Image/ImageBase{TColor}.cs
  61. 251
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  62. 104
      src/ImageSharp/Image/PixelArea{TColor}.cs
  63. 28
      src/ImageSharp/PixelAccessor.cs
  64. 50
      src/ImageSharp/Profiles/Exif/ExifTag.cs
  65. 14
      src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs
  66. 2
      src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs
  67. 15
      src/ImageSharp/Quantizers/Wu/WuQuantizer.cs
  68. 4
      src/ImageSharp/project.json
  69. 21
      tests/ImageSharp.Benchmarks/Image/CopyPixels.cs
  70. 2
      tests/ImageSharp.Tests/Colors/ColorConversionTests.cs
  71. 165
      tests/ImageSharp.Tests/Colors/PackedPixelTests.cs
  72. 18
      tests/ImageSharp.Tests/Common/ConstantsTests.cs
  73. 16
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  74. 6
      tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
  75. 8
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
  76. 2
      tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs
  77. 24
      tests/ImageSharp.Tests/Image/PixelAccessorTests.cs
  78. 12
      tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs
  79. 8
      tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs
  80. 7
      tests/ImageSharp.Tests/project.json
  81. 29
      tests/ImageSharp.Tests46/HelloTest.cs
  82. 179
      tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
  83. 36
      tests/ImageSharp.Tests46/Properties/AssemblyInfo.cs
  84. 78
      tests/ImageSharp.Tests46/TestFile.cs
  85. 10
      tests/ImageSharp.Tests46/packages.config

6
ImageSharp.ruleset

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="ImageSharp" ToolsVersion="14.0">
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1413" Action="None" />
</Rules>
</RuleSet>

10
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

1
ImageSharp.sln.DotSettings

@ -345,6 +345,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FDCT/@EntryIndexedValue">FDCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IDCT/@EntryIndexedValue">IDCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JPEG/@EntryIndexedValue">JPEG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MCU/@EntryIndexedValue">MCU</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PNG/@EntryIndexedValue">PNG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RGB/@EntryIndexedValue">RGB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RLE/@EntryIndexedValue">RLE</s:String>

3
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)

2
global.json

@ -1,6 +1,6 @@
{
"projects": [ "src" ],
"sdk": {
"version": "1.0.0-preview2-003121"
"version": "1.0.0-preview2-003131"
}
}

58
src/ImageSharp/Colors/Color.cs

@ -273,35 +273,37 @@ namespace ImageSharp
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public void ToZyxBytes(byte[] bytes, int startIndex)
{
bytes[startIndex] = this.B;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.R;
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>

20
src/ImageSharp/Colors/ColorspaceTransforms.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.Numerics;
using Colors.Spaces;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
@ -18,11 +19,6 @@ namespace ImageSharp
/// </remarks>
public partial struct Color
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001F;
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>.
@ -62,9 +58,9 @@ namespace ImageSharp
/// </returns>
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;
}

8
src/ImageSharp/Colors/ComponentOrder.cs

@ -13,21 +13,21 @@ namespace ImageSharp
/// <summary>
/// Z-> Y-> X order. Equivalent to B-> G-> R in <see cref="Color"/>
/// </summary>
ZYX,
Zyx,
/// <summary>
/// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in <see cref="Color"/>
/// </summary>
ZYXW,
Zyxw,
/// <summary>
/// X-> Y-> Z order. Equivalent to R-> G-> B in <see cref="Color"/>
/// </summary>
XYZ,
Xyz,
/// <summary>
/// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in <see cref="Color"/>
/// </summary>
XYZW,
Xyzw,
}
}

58
src/ImageSharp/Colors/PackedPixel/Alpha8.cs

@ -74,35 +74,37 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public void ToXyzwBytes(byte[] bytes, int startIndex)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = this.PackedValue;
}
/// <inheritdoc />
public void ToZyxBytes(byte[] bytes, int startIndex)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
}
/// <inheritdoc />
public void ToZyxwBytes(byte[] bytes, int startIndex)
{
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = this.PackedValue;
}
/// <summary>

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

@ -220,36 +220,38 @@ namespace ImageSharp
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc/>
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public void ToZyxBytes(byte[] bytes, int startIndex)
{
bytes[startIndex] = this.B;
bytes[startIndex + 1] = this.G;
bytes[startIndex + 2] = this.R;
}
/// <inheritdoc />
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;
}
/// <inheritdoc/>

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

@ -96,37 +96,41 @@ namespace ImageSharp
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
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);
}
/// <inheritdoc />

60
src/ImageSharp/Colors/PackedPixel/Bgra4444.cs

@ -88,37 +88,41 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

60
src/ImageSharp/Colors/PackedPixel/Bgra5551.cs

@ -88,37 +88,41 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

60
src/ImageSharp/Colors/PackedPixel/Byte4.cs

@ -95,37 +95,41 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

73
src/ImageSharp/Colors/PackedPixel/HalfSingle.cs

@ -97,40 +97,57 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

73
src/ImageSharp/Colors/PackedPixel/HalfVector2.cs

@ -111,40 +111,57 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

73
src/ImageSharp/Colors/PackedPixel/HalfVector4.cs

@ -105,40 +105,57 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

32
src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs

@ -12,7 +12,7 @@ namespace ImageSharp
public interface IPackedBytes
{
/// <summary>
/// Gets the packed representation from the gives bytes.
/// Sets the packed representation from the given byte array.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
@ -21,11 +21,35 @@ namespace ImageSharp
void PackFromBytes(byte x, byte y, byte z, byte w);
/// <summary>
/// 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 <see cref="Color"/>
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
/// <param name="componentOrder">The order of the components.</param>
void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder);
void ToXyzBytes(byte[] bytes, int startIndex);
/// <summary>
/// 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 <see cref="Color"/>
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToXyzwBytes(byte[] bytes, int startIndex);
/// <summary>
/// Expands the packed representation into a given byte array.
/// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in <see cref="Color"/>
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToZyxBytes(byte[] bytes, int startIndex);
/// <summary>
/// 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 <see cref="Color"/>
/// </summary>
/// <param name="bytes">The bytes to set the color in.</param>
/// <param name="startIndex">The starting index of the <paramref name="bytes"/>.</param>
void ToZyxwBytes(byte[] bytes, int startIndex);
}
}

79
src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs

@ -120,7 +120,7 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

79
src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs

@ -114,7 +114,7 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

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

@ -108,7 +108,7 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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;
}
/// <summary>

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

@ -116,7 +116,7 @@ namespace ImageSharp
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />

64
src/ImageSharp/Colors/PackedPixel/Rg32.cs

@ -100,37 +100,45 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />

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

@ -95,37 +95,45 @@ namespace ImageSharp
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />

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

@ -94,37 +94,45 @@ namespace ImageSharp
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />

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

@ -106,7 +106,7 @@ namespace ImageSharp
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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;
}
/// <summary>

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

@ -112,7 +112,7 @@ namespace ImageSharp
}
/// <inheritdoc />
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);
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a specified object.
/// </summary>
/// <param name="obj">The object with which to make the comparison.</param>
/// <returns>true if the current instance is equal to the specified object; false otherwise.</returns>
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Short4) && this == (Short4)obj;
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a specified object.
/// </summary>
/// <param name="other">The object with which to make the comparison.</param>
/// <returns>true if the current instance is equal to the specified object; false otherwise.</returns>
/// <inheritdoc />
public bool Equals(Short4 other)
{
return this == other;

17
src/ImageSharp/Colors/Colorspaces/Bgra32.cs → src/ImageSharp/Colors/Spaces/Bgra32.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@ -22,7 +22,7 @@ namespace ImageSharp
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
private readonly Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
@ -133,7 +133,7 @@ namespace ImageSharp
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
@ -152,16 +152,5 @@ namespace ImageSharp
{
return this.backingVector.Equals(other.backingVector);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Bgra32"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(Bgra32 color) => color.backingVector.GetHashCode();
}
}

24
src/ImageSharp/Colors/Colorspaces/CieLab.cs → src/ImageSharp/Colors/Spaces/CieLab.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@ -20,15 +20,10 @@ namespace ImageSharp
/// </summary>
public static readonly CieLab Empty = default(CieLab);
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector3 backingVector;
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct.
@ -138,7 +133,7 @@ namespace ImageSharp
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
@ -166,7 +161,7 @@ namespace ImageSharp
/// <inheritdoc/>
public bool Equals(CieLab other)
{
return this.AlmostEquals(other, Epsilon);
return this.AlmostEquals(other, Constants.Epsilon);
}
/// <inheritdoc/>
@ -178,16 +173,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="CieLab"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(CieLab color) => color.backingVector.GetHashCode();
}
}

24
src/ImageSharp/Colors/Colorspaces/CieXyz.cs → src/ImageSharp/Colors/Spaces/CieXyz.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@ -20,15 +20,10 @@ namespace ImageSharp
/// </summary>
public static readonly CieXyz Empty = default(CieXyz);
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector3 backingVector;
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
@ -129,7 +124,7 @@ namespace ImageSharp
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
@ -157,7 +152,7 @@ namespace ImageSharp
/// <inheritdoc/>
public bool Equals(CieXyz other)
{
return this.AlmostEquals(other, Epsilon);
return this.AlmostEquals(other, Constants.Epsilon);
}
/// <inheritdoc/>
@ -169,16 +164,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Hsv"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(CieXyz color) => color.backingVector.GetHashCode();
}
}

26
src/ImageSharp/Colors/Colorspaces/Cmyk.cs → src/ImageSharp/Colors/Spaces/Cmyk.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@ -19,15 +19,10 @@ namespace ImageSharp
/// </summary>
public static readonly Cmyk Empty = default(Cmyk);
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
private readonly Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> 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
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
@ -167,7 +162,7 @@ namespace ImageSharp
/// <inheritdoc/>
public bool Equals(Cmyk other)
{
return this.AlmostEquals(other, Epsilon);
return this.AlmostEquals(other, Constants.Epsilon);
}
/// <inheritdoc/>
@ -180,16 +175,5 @@ namespace ImageSharp
&& result.Z < precision
&& result.W < precision;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Cmyk"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(Cmyk color) => color.backingVector.GetHashCode();
}
}

32
src/ImageSharp/Colors/Colorspaces/Hsl.cs → src/ImageSharp/Colors/Spaces/Hsl.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@ -19,15 +19,10 @@ namespace ImageSharp
/// </summary>
public static readonly Hsl Empty = default(Hsl);
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001F;
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector3 backingVector;
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> 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
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
@ -186,7 +181,7 @@ namespace ImageSharp
/// <inheritdoc/>
public bool Equals(Hsl other)
{
return this.AlmostEquals(other, Epsilon);
return this.AlmostEquals(other, Constants.Epsilon);
}
/// <inheritdoc/>
@ -198,16 +193,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Hsl"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(Hsl color) => color.backingVector.GetHashCode();
}
}

32
src/ImageSharp/Colors/Colorspaces/Hsv.cs → src/ImageSharp/Colors/Spaces/Hsv.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
@ -19,15 +19,10 @@ namespace ImageSharp
/// </summary>
public static readonly Hsv Empty = default(Hsv);
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001F;
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector3 backingVector;
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> 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
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
@ -179,7 +174,7 @@ namespace ImageSharp
/// <inheritdoc/>
public bool Equals(Hsv other)
{
return this.AlmostEquals(other, Epsilon);
return this.AlmostEquals(other, Constants.Epsilon);
}
/// <inheritdoc/>
@ -191,16 +186,5 @@ namespace ImageSharp
&& result.Y < precision
&& result.Z < precision;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Hsv"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private static int GetHashCode(Hsv color) => color.backingVector.GetHashCode();
}
}

2
src/ImageSharp/Colors/Colorspaces/IAlmostEquatable.cs → src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;

33
src/ImageSharp/Colors/Colorspaces/YCbCr.cs → src/ImageSharp/Colors/Spaces/YCbCr.cs

@ -3,10 +3,11 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
namespace ImageSharp.Colors.Spaces
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// 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
/// </summary>
public static readonly YCbCr Empty = default(YCbCr);
/// <summary>
/// Vector which is used in clamping to the max value
/// </summary>
private static readonly Vector3 VectorMax = new Vector3(255);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
/// </summary>
@ -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);
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public byte Y { get; }
public float Y => this.backingVector.X;
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public byte Cb { get; }
public float Cb => this.backingVector.Y;
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public byte Cr { get; }
public float Cr => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="YCbCr"/> is empty.
@ -117,13 +126,7 @@ namespace ImageSharp
/// <inheritdoc/>
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();
}
/// <inheritdoc/>
@ -151,7 +154,7 @@ namespace ImageSharp
/// <inheritdoc/>
public bool Equals(YCbCr other)
{
return this.Y == other.Y && this.Cb == other.Cb && this.Cr == other.Cr;
return this.backingVector.Equals(other.backingVector);
}
}
}

13
src/ImageSharp/Colors/Vector4BlendTransforms.cs

@ -14,11 +14,6 @@ namespace ImageSharp
/// </summary>
public class Vector4BlendTransforms
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.0001F;
/// <summary>
/// The blending formula simply selects the source vector.
/// </summary>
@ -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
/// </returns>
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);
}
/// <summary>
@ -279,7 +274,7 @@ namespace ImageSharp
/// </returns>
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);
}
/// <summary>

18
src/ImageSharp/Common/Constants.cs

@ -0,0 +1,18 @@
// <copyright file="Constants.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Common constants used throughout the project
/// </summary>
internal static class Constants
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
public static readonly float Epsilon = 0.001f;
}
}

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

@ -91,9 +91,7 @@ namespace ImageSharp
/// </returns>
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<TColor>(ImageBase<TColor> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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
/// </returns>.
private static float Clean(float x)
{
const float Epsilon = .00001F;
if (Math.Abs(x) < Epsilon)
if (Math.Abs(x) < Constants.Epsilon)
{
return 0F;
}

4
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<TColor> 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;

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

@ -21,8 +21,6 @@ namespace ImageSharp.Drawing.Processors
public class FillShapeProcessor<TColor> : ImageFilteringProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
private const float Epsilon = 0.001f;
private const float AntialiasFactor = 1f;
private const int DrawPadding = 1;
private readonly IBrush<TColor> 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();

7
src/ImageSharp/Filters/Processors/Effects/BackgroundColorProcessor.cs

@ -16,11 +16,6 @@ namespace ImageSharp.Processors
public class BackgroundColorProcessor<TColor> : ImageFilteringProcessor<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor{TColor}"/> class.
/// </summary>
@ -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;
}

13
src/ImageSharp/Filters/Processors/Transforms/RotateProcessor.cs

@ -70,9 +70,7 @@ namespace ImageSharp.Processors
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TColor> 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
/// <returns>The <see cref="bool"/></returns>
private bool OptimizedApply(ImageBase<TColor> 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;

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

@ -275,7 +275,7 @@ namespace ImageSharp.Formats
const int ComponentCount = 2;
TColor color = default(TColor);
using (PixelArea<TColor> row = new PixelArea<TColor>(width, ComponentOrder.XYZ))
using (PixelArea<TColor> row = new PixelArea<TColor>(width, ComponentOrder.Xyz))
{
for (int y = 0; y < height; y++)
{
@ -312,7 +312,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
int padding = CalculatePadding(width, 3);
using (PixelArea<TColor> row = new PixelArea<TColor>(width, ComponentOrder.ZYX, padding))
using (PixelArea<TColor> row = new PixelArea<TColor>(width, ComponentOrder.Zyx, padding))
{
for (int y = 0; y < height; y++)
{
@ -336,7 +336,7 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
int padding = CalculatePadding(width, 4);
using (PixelArea<TColor> row = new PixelArea<TColor>(width, ComponentOrder.ZYXW, padding))
using (PixelArea<TColor> row = new PixelArea<TColor>(width, ComponentOrder.Zyxw, padding))
{
for (int y = 0; y < height; y++)
{

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

@ -150,12 +150,12 @@ namespace ImageSharp.Formats
private void Write32Bit<TColor>(EndianBinaryWriter writer, PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
using (PixelArea<TColor> row = new PixelArea<TColor>(pixels.Width, ComponentOrder.ZYXW, this.padding))
using (PixelArea<TColor> row = new PixelArea<TColor>(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<TColor>(EndianBinaryWriter writer, PixelAccessor<TColor> pixels)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
using (PixelArea<TColor> row = new PixelArea<TColor>(pixels.Width, ComponentOrder.ZYX, this.padding))
using (PixelArea<TColor> row = new PixelArea<TColor>(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);
}
}
}

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

@ -451,7 +451,7 @@ namespace ImageSharp.Formats
}
else
{
using (PixelArea<TColor> emptyRow = new PixelArea<TColor>(this.restoreArea.Value.Width, ComponentOrder.XYZW))
using (PixelArea<TColor> emptyRow = new PixelArea<TColor>(this.restoreArea.Value.Width, ComponentOrder.Xyzw))
{
using (PixelAccessor<TColor> pixelAccessor = frame.Lock())
{

7
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<TColor>
{
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];

7
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));
}
}
}

101
src/ImageSharp/Formats/Jpg/Components/Decoder/GrayImage.cs

@ -1,101 +0,0 @@
// <copyright file="GrayImage.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpg
{
/// <summary>
/// Represents a grayscale image
/// </summary>
internal class GrayImage
{
/// <summary>
/// Initializes a new instance of the <see cref="GrayImage"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public GrayImage(int width, int height)
{
this.Width = width;
this.Height = height;
this.Pixels = new byte[width * height];
this.Stride = width;
this.Offset = 0;
}
/// <summary>
/// Prevents a default instance of the <see cref="GrayImage"/> class from being created.
/// </summary>
private GrayImage()
{
}
/// <summary>
/// Gets or sets the pixels.
/// </summary>
public byte[] Pixels { get; set; }
/// <summary>
/// Gets or sets the stride.
/// </summary>
public int Stride { get; set; }
/// <summary>
/// Gets or sets the horizontal position.
/// </summary>
public int X { get; set; }
/// <summary>
/// Gets or sets the vertical position.
/// </summary>
public int Y { get; set; }
/// <summary>
/// Gets or sets the width.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the height.
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets or sets the offset
/// </summary>
public int Offset { get; set; }
/// <summary>
/// Gets an image made up of a subset of the originals pixels.
/// </summary>
/// <param name="x">The x-coordinate of the image.</param>
/// <param name="y">The y-coordinate of the image.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>
/// The <see cref="GrayImage"/>.
/// </returns>
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
};
}
/// <summary>
/// Gets the row offset at the given position
/// </summary>
/// <param name="y">The y-coordinate of the image.</param>
/// <returns>The <see cref="int"/></returns>
public int GetRowOffset(int y)
{
return this.Offset + (y * this.Stride);
}
}
}

199
src/ImageSharp/Formats/Jpg/Components/Decoder/HuffmanTree.cs

@ -8,68 +8,118 @@ namespace ImageSharp.Formats.Jpg
using System.Buffers;
/// <summary>
/// Represents a Huffman tree
/// Represents a Huffman tree
/// </summary>
internal struct HuffmanTree : IDisposable
{
/// <summary>
/// Gets or sets the number of codes in the tree.
/// The maximum (inclusive) number of codes in a Huffman tree.
/// </summary>
public const int MaxNCodes = 256;
/// <summary>
/// The maximum (inclusive) number of bits in a Huffman code.
/// </summary>
public const int MaxCodeLength = 16;
/// <summary>
/// The maximum number of Huffman table classes
/// </summary>
public const int MaxTc = 1;
/// <summary>
/// The maximum number of Huffman table identifiers
/// </summary>
public const int MaxTh = 3;
/// <summary>
/// Row size of the Huffman table
/// </summary>
public const int ThRowSize = MaxTh + 1;
/// <summary>
/// Number of Hufman Trees in the Huffman table
/// </summary>
public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1);
/// <summary>
/// The log-2 size of the Huffman decoder's look-up table.
/// </summary>
public const int LutSize = 8;
/// <summary>
/// Gets or sets the number of codes in the tree.
/// </summary>
public int Length;
/// <summary>
/// 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.
/// </summary>
public ushort[] Lut;
/// <summary>
/// Gets the the decoded values, sorted by their encoding.
/// Gets the the decoded values, sorted by their encoding.
/// </summary>
public byte[] Values;
/// <summary>
/// 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.
/// </summary>
public int[] MinCodes;
/// <summary>
/// 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.
/// </summary>
public int[] MaxCodes;
/// <summary>
/// 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].
/// </summary>
public int[] Indices;
private static readonly ArrayPool<ushort> UshortBuffer = ArrayPool<ushort>.Create(1 << JpegDecoderCore.LutSize, 50);
private static readonly ArrayPool<ushort> UshortBuffer = ArrayPool<ushort>.Create(1 << LutSize, 50);
private static readonly ArrayPool<byte> ByteBuffer = ArrayPool<byte>.Create(JpegDecoderCore.MaxNCodes, 50);
private static readonly ArrayPool<byte> ByteBuffer = ArrayPool<byte>.Create(MaxNCodes, 50);
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(JpegDecoderCore.MaxCodeLength, 50);
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(MaxCodeLength, 50);
/// <summary>
/// Initializes the Huffman tree
/// Creates and initializes an array of <see cref="HuffmanTree" /> instances of size <see cref="NumberOfTrees" />
/// </summary>
/// <param name="lutSize">Lut size</param>
/// <param name="maxNCodes">Max N codes</param>
/// <param name="maxCodeLength">Max code length</param>
public void Init(int lutSize, int maxNCodes, int maxCodeLength)
/// <returns>An array of <see cref="HuffmanTree" /> instances representing the Huffman tables</returns>
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;
}
/// <summary>
/// Disposes the underlying buffers
/// Initializes the Huffman tree
/// </summary>
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);
}
/// <summary>
/// Disposes the underlying buffers
/// </summary>
public void Dispose()
{
@ -79,5 +129,102 @@ namespace ImageSharp.Formats.Jpg
IntBuffer.Return(this.MaxCodes, true);
IntBuffer.Return(this.Indices, true);
}
/// <summary>
/// Internal part of the DHT processor, whatever does it mean
/// </summary>
/// <param name="decoder">The decoder instance</param>
/// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param>
/// <param name="remaining">Remaining bits</param>
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;
}
}
}
}

132
src/ImageSharp/Formats/Jpg/Components/Decoder/JpegPixelArea.cs

@ -0,0 +1,132 @@
// <copyright file="JpegPixelArea.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpg
{
using System.Runtime.CompilerServices;
/// <summary>
/// Represents an area of a Jpeg subimage (channel)
/// </summary>
internal struct JpegPixelArea
{
/// <summary>
/// Initializes a new instance of the <see cref="JpegPixelArea" /> struct from existing data.
/// </summary>
/// <param name="pixels">The pixel array</param>
/// <param name="striede">The stride</param>
/// <param name="offset">The offset</param>
public JpegPixelArea(byte[] pixels, int striede, int offset)
{
this.Stride = striede;
this.Pixels = pixels;
this.Offset = offset;
}
/// <summary>
/// Gets the pixels.
/// </summary>
public byte[] Pixels { get; private set; }
/// <summary>
/// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea))
/// </summary>
public bool IsInitialized => this.Pixels != null;
/// <summary>
/// Gets or the stride.
/// </summary>
public int Stride { get; }
/// <summary>
/// Gets or the offset.
/// </summary>
public int Offset { get; }
/// <summary>
/// Gets a <see cref="MutableSpan{T}" /> of bytes to the pixel area
/// </summary>
public MutableSpan<byte> Span => new MutableSpan<byte>(this.Pixels, this.Offset);
/// <summary>
/// Returns the pixel at (x, y)
/// </summary>
/// <param name="x">The x index</param>
/// <param name="y">The y index</param>
/// <returns>The pixel value</returns>
public byte this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Pixels[(y * this.Stride) + x];
}
}
/// <summary>
/// Creates a new instance of the <see cref="JpegPixelArea" /> struct.
/// Pixel array will be taken from a pool, this instance will be the owner of it's pixel data, therefore
/// <see cref="ReturnPooled" /> should be called when the instance is no longer needed.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>A <see cref="JpegPixelArea" /> with pooled data</returns>
public static JpegPixelArea CreatePooled(int width, int height)
{
int size = width * height;
var pixels = CleanPooler<byte>.RentCleanArray(size);
return new JpegPixelArea(pixels, width, 0);
}
/// <summary>
/// Returns <see cref="Pixels" /> to the pool
/// </summary>
public void ReturnPooled()
{
if (this.Pixels == null)
{
return;
}
CleanPooler<byte>.ReturnArray(this.Pixels);
this.Pixels = null;
}
/// <summary>
/// Gets the subarea that belongs to the Block8x8 defined by block indices
/// </summary>
/// <param name="bx">The block X index</param>
/// <param name="by">The block Y index</param>
/// <returns>The subarea offseted by block indices</returns>
public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by)
{
int offset = this.Offset + (8 * ((by * this.Stride) + bx));
return new JpegPixelArea(this.Pixels, this.Stride, offset);
}
/// <summary>
/// Gets the row offset at the given position
/// </summary>
/// <param name="y">The y-coordinate of the image.</param>
/// <returns>The <see cref="int" /></returns>
public int GetRowOffset(int y)
{
return this.Offset + (y * this.Stride);
}
/// <summary>
/// Load values to the pixel area from the given <see cref="Block8x8F" />.
/// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to
/// <see cref="byte" /> values
/// </summary>
/// <param name="block">The block holding the color values</param>
/// <param name="temp">Temporal block provided by the caller</param>
[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<byte>(this.Pixels, this.Offset), this.Stride, temp);
}
}
}

653
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;
/// <summary>
/// Encapsulates the impementation of Jpeg SOS decoder.
/// See JpegScanDecoder.md!
/// </summary>
internal unsafe struct JpegScanDecoder
{
/// <summary>
/// The AC table index
/// </summary>
internal const int AcTableIndex = 1;
/// <summary>
/// The DC table index
/// </summary>
internal const int DcTableIndex = 0;
/// <summary>
/// Holds the "large" data blocks needed for computations
/// </summary>
[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;
}
}
/// <summary>
/// Contains pointers to the memory regions of <see cref="ComponentData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
/// </summary>
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).
/// <summary>
/// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
/// </summary>
private int bx;
/// <summary>
/// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
/// </summary>
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
}
}
/// <summary>
/// Decodes a successive approximation refinement block, as specified in section G.1.2.
/// </summary>
/// <param name="h">The Huffman tree</param>
/// <param name="delta">The low transform offset</param>
/// <param name="decoder">The decoder instance</param>
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);
}
}
/// <summary>
/// Refines non-zero entries of b in zig-zag order.
/// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over.
/// </summary>
/// <param name="decoder">The decoder</param>
/// <param name="zig">The zig-zag start index</param>
/// <param name="nz">The non-zero entry</param>
/// <param name="delta">The low transform offset</param>
/// <returns>The <see cref="int" /></returns>
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);
}
}
}

25
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`

26
src/ImageSharp/Formats/Jpg/Components/Decoder/Scan.cs

@ -0,0 +1,26 @@
namespace ImageSharp.Formats.Jpg
{
using System.Runtime.InteropServices;
/// <summary>
/// Represents a component scan
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct Scan
{
/// <summary>
/// Gets or sets the component index.
/// </summary>
public byte Index;
/// <summary>
/// Gets or sets the DC table selector
/// </summary>
public byte DcTableSelector;
/// <summary>
/// Gets or sets the AC table selector
/// </summary>
public byte AcTableSelector;
}
}

160
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.
// </copyright>
namespace ImageSharp.Formats.Jpg
{
using System;
/// <summary>
/// Represents an image made up of three color components (luminance, blue chroma, red chroma)
/// Represents an image made up of three color components (luminance, blue chroma, red chroma)
/// </summary>
internal class YCbCrImage
internal class YCbCrImage : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="YCbCrImage"/> class.
/// Initializes a new instance of the <see cref="YCbCrImage" /> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
@ -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<byte>.RentCleanArray(width * height);
this.CbPixels = CleanPooler<byte>.RentCleanArray(cw * ch);
this.CrPixels = CleanPooler<byte>.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;
}
/// <summary>
/// Prevents a default instance of the <see cref="YCbCrImage"/> class from being created.
/// </summary>
private YCbCrImage()
{
}
/// <summary>
/// Provides enumeration of the various available subsample ratios.
/// Provides enumeration of the various available subsample ratios.
/// </summary>
public enum YCbCrSubsampleRatio
{
/// <summary>
/// YCbCrSubsampleRatio444
/// YCbCrSubsampleRatio444
/// </summary>
YCbCrSubsampleRatio444,
/// <summary>
/// YCbCrSubsampleRatio422
/// YCbCrSubsampleRatio422
/// </summary>
YCbCrSubsampleRatio422,
/// <summary>
/// YCbCrSubsampleRatio420
/// YCbCrSubsampleRatio420
/// </summary>
YCbCrSubsampleRatio420,
/// <summary>
/// YCbCrSubsampleRatio440
/// YCbCrSubsampleRatio440
/// </summary>
YCbCrSubsampleRatio440,
/// <summary>
/// YCbCrSubsampleRatio411
/// YCbCrSubsampleRatio411
/// </summary>
YCbCrSubsampleRatio411,
/// <summary>
/// YCbCrSubsampleRatio410
/// YCbCrSubsampleRatio410
/// </summary>
YCbCrSubsampleRatio410,
}
/// <summary>
/// Gets or sets the luminance components channel.
/// </summary>
public byte[] YChannel { get; set; }
/// <summary>
/// Gets or sets the blue chroma components channel.
/// Gets an offseted <see cref="JpegPixelArea" /> to the Cb channel
/// </summary>
public byte[] CbChannel { get; set; }
public JpegPixelArea CbChannel => new JpegPixelArea(this.CbPixels, this.CStride, this.COffset);
/// <summary>
/// Gets or sets the red chroma components channel.
/// Gets the blue chroma components channel.
/// </summary>
public byte[] CrChannel { get; set; }
public byte[] CbPixels { get; }
/// <summary>
/// Gets or sets the Y slice index delta between vertically adjacent pixels.
/// Gets the index of the first element of red or blue chroma.
/// </summary>
public int YStride { get; set; }
public int COffset { get; }
/// <summary>
/// Gets or sets the red and blue chroma slice index delta between vertically adjacent pixels
/// that map to separate chroma samples.
/// Gets an offseted <see cref="JpegPixelArea" /> to the Cr channel
/// </summary>
public int CStride { get; set; }
public JpegPixelArea CrChannel => new JpegPixelArea(this.CrPixels, this.CStride, this.COffset);
/// <summary>
/// Gets or sets the index of the first luminance element.
/// Gets the red chroma components channel.
/// </summary>
public int YOffset { get; set; }
public byte[] CrPixels { get; }
/// <summary>
/// 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.
/// </summary>
public int COffset { get; set; }
public int CStride { get; }
/// <summary>
/// Gets or sets the horizontal position.
/// Gets or sets the subsampling ratio.
/// </summary>
public int X { get; set; }
public YCbCrSubsampleRatio Ratio { get; set; }
/// <summary>
/// Gets or sets the vertical position.
/// Gets an offseted <see cref="JpegPixelArea" /> to the Y channel
/// </summary>
public int Y { get; set; }
public JpegPixelArea YChannel => new JpegPixelArea(this.YPixels, this.YStride, this.YOffset);
/// <summary>
/// Gets or sets the width.
/// Gets the index of the first luminance element.
/// </summary>
public int Width { get; set; }
public int YOffset { get; }
/// <summary>
/// Gets or sets the height.
/// Gets the luminance components channel.
/// </summary>
public int Height { get; set; }
public byte[] YPixels { get; }
/// <summary>
/// Gets or sets the subsampling ratio.
/// Gets the Y slice index delta between vertically adjacent pixels.
/// </summary>
public YCbCrSubsampleRatio Ratio { get; set; }
public int YStride { get; }
/// <summary>
/// Gets an image made up of a subset of the originals pixels.
/// Disposes the <see cref="YCbCrImage" /> returning rented arrays to the pools.
/// </summary>
/// <param name="x">The x-coordinate of the image.</param>
/// <param name="y">The y-coordinate of the image.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns>
/// The <see cref="YCbCrImage"/>.
/// </returns>
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<byte>.ReturnArray(this.YPixels);
CleanPooler<byte>.ReturnArray(this.CrPixels);
CleanPooler<byte>.ReturnArray(this.CbPixels);
}
/// <summary>
/// Returns the offset of the first luminance component at the given row
/// Returns the offset of the first chroma component at the given row
/// </summary>
/// <param name="y">The row number.</param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int GetRowYOffset(int y)
{
return y * this.YStride;
}
/// <summary>
/// Returns the offset of the first chroma component at the given row
/// </summary>
/// <param name="y">The row number.</param>
/// <returns>
/// The <see cref="int"/>.
/// The <see cref="int" />.
/// </returns>
public int GetRowCOffset(int y)
{
@ -203,14 +160,31 @@ namespace ImageSharp.Formats.Jpg
}
/// <summary>
/// Returns the height and width of the chroma components
/// Returns the offset of the first luminance component at the given row
/// </summary>
/// <param name="y">The row number.</param>
/// <returns>
/// The <see cref="int" />.
/// </returns>
public int GetRowYOffset(int y)
{
return y * this.YStride;
}
/// <summary>
/// Returns the height and width of the chroma components
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="ratio">The subsampling ratio.</param>
/// <param name="chromaWidth">The chroma width.</param>
/// <param name="chromaHeight">The chroma height.</param>
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
}
}
}
}
}

2414
src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs

File diff suppressed because it is too large

21
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.
// </copyright>
namespace ImageSharp.Formats
{
using System;
@ -230,18 +231,24 @@ namespace ImageSharp.Formats
/// Writes data to "Define Quantization Tables" block for QuantIndex
/// </summary>
/// <param name="dqt">The "Define Quantization Tables" block</param>
/// <param name="offset">Offset in dqt</param>
/// <param name="offset">Offset in "Define Quantization Tables" block</param>
/// <param name="i">The quantization index</param>
/// <param name="q">The quantazation table to copy data from</param>
private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F q)
/// <param name="quant">The quantization table to copy data from</param>
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];
}
}
/// <summary>
/// Initializes quantization table.
/// </summary>
/// <param name="i">The quantization index.</param>
/// <param name="scale">The scaling factor.</param>
/// <param name="quant">The quantization table.</param>
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<TColor> rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ, true))
using (PixelArea<TColor> rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz))
{
for (int y = 0; y < pixels.Height; y += 8)
{
@ -517,7 +524,7 @@ namespace ImageSharp.Formats
/// <param name="tempDest">Temporal block to be used as FDCT Destination</param>
/// <param name="temp2">Temporal block 2</param>
/// <param name="quant">Quantization table</param>
/// <param name="unzigPtr">The 8x8 Unzig block ptr</param>
/// <param name="unzigPtr">The 8x8 Unzig block pointer</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
@ -798,7 +805,7 @@ namespace ImageSharp.Formats
// ReSharper disable once InconsistentNaming
float prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
using (PixelArea<TColor> rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ, true))
using (PixelArea<TColor> rgbBytes = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz))
{
for (int y = 0; y < pixels.Height; y += 16)
{

30
src/ImageSharp/Formats/Jpg/Utils/CleanPooler.cs

@ -0,0 +1,30 @@
// <copyright file="CleanPooler.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System.Buffers;
/// <summary>
/// Wraps <see cref="ArrayPool{T}"/> to always provide arrays initialized with default(T)
/// </summary>
/// <typeparam name="T">The element type</typeparam>
internal class CleanPooler<T>
{
private static readonly ArrayPool<T> Pool = ArrayPool<T>.Create();
/// <summary>
/// Rents a clean array
/// </summary>
/// <param name="minimumLength">The minimum array length</param>
/// <returns>A clean array of T</returns>
public static T[] RentCleanArray(int minimumLength) => Pool.Rent(minimumLength);
/// <summary>
/// Retursn array to the pool
/// </summary>
/// <param name="array">The array</param>
public static void ReturnArray(T[] array) => Pool.Return(array, true);
}
}

37
src/ImageSharp/Formats/Jpg/Utils/JpegUtils.cs

@ -27,41 +27,12 @@ namespace ImageSharp.Formats.Jpg
int sourceX)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
pixels.UncheckedCopyTo(dest, sourceY, sourceX);
pixels.SafeCopyTo(dest, sourceY, sourceX);
int stretchFromX = pixels.Width - sourceX;
int stretchFromY = pixels.Height - sourceY;
StretchPixels(dest, stretchFromX, stretchFromY);
}
/// <summary>
/// Copy a region of image into the image destination area. Does not throw when requesting a 0-size copy.
/// </summary>
/// <typeparam name="TColor">The pixel type</typeparam>
/// <param name="sourcePixels">The source <see cref="PixelAccessor{TColor}"/> </param>
/// <param name="destinationArea">The destination area.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
public static void UncheckedCopyTo<TColor>(
this PixelAccessor<TColor> sourcePixels,
PixelArea<TColor> destinationArea,
int sourceY,
int sourceX)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
// TODO: Code smell! This is exactly the same code PixelArea<TColor>.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);
}
/// <summary>
/// Copy an RGB value
/// </summary>
@ -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++)
{

6
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<TColor>
{
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
using (PixelArea<TColor> pixelRow = new PixelArea<TColor>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ))
using (PixelArea<TColor> pixelRow = new PixelArea<TColor>(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];

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

@ -64,7 +64,7 @@ namespace ImageSharp
using (PixelAccessor<TColor> sourcePixels = other.Lock())
using (PixelAccessor<TColor> target = this.Lock())
{
sourcePixels.CopyImage(target);
sourcePixels.CopyTo(target);
}
}

251
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -145,69 +145,40 @@ namespace ImageSharp
}
/// <summary>
/// Copies a block of pixels at the specified position.
/// Copy an area of pixels to the image.
/// </summary>
/// <param name="sourceX">The x-coordinate of the source image.</param>
/// <param name="sourceY">The y-coordinate of the source image.</param>
/// <param name="target">The target pixel buffer accessor.</param>
/// <param name="targetX">The x-coordinate of the target image.</param>
/// <param name="targetY">The y-coordinate of the target image.</param>
/// <param name="pixelCount">The number of pixels to copy</param>
public void CopyBlock(int sourceX, int sourceY, PixelAccessor<TColor> target, int targetX, int targetY, int pixelCount)
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
public void CopyFrom(PixelArea<TColor> area, int targetY, int targetX = 0)
{
int size = Unsafe.SizeOf<TColor>();
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);
/// <summary>
/// Copies an entire image.
/// </summary>
/// <param name="target">The target pixel buffer accessor.</param>
public void CopyImage(PixelAccessor<TColor> target)
{
this.CopyBlock(0, 0, target, 0, 0, target.Width * target.Height);
this.CopyFrom(area, targetX, targetY, area.Width, area.Height);
}
/// <summary>
/// Copied a row of pixels from the image.
/// Copy pixels from the image to an area of pixels.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
public void CopyFrom(PixelArea<TColor> area, int targetY, int targetX = 0)
public void CopyTo(PixelArea<TColor> 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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="sourceY">The source row index.</param>
@ -215,29 +186,21 @@ namespace ImageSharp
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
public void CopyTo(PixelArea<TColor> area, int sourceY, int sourceX = 0)
public void SafeCopyTo(PixelArea<TColor> 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);
}
/// <summary>
@ -269,6 +232,17 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
/// <summary>
/// Copies the pixels to another <see cref="PixelAccessor{TColor}"/> of the same size.
/// </summary>
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TColor> target)
{
uint byteCount = (uint)(this.Width * this.Height * Unsafe.SizeOf<TColor>());
Unsafe.CopyBlock(target.pixelsBase, this.pixelsBase, byteCount);
}
/// <summary>
/// Resets all the pixels to it's initial value.
/// </summary>
@ -278,21 +252,21 @@ namespace ImageSharp
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.ZYX"/> format.
/// Copies from an area in <see cref="ComponentOrder.Zyx"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyFromZYX(PixelArea<TColor> area, int targetY, int targetX, int width, int height)
protected virtual void CopyFromZyx(PixelArea<TColor> area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
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
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.ZYXW"/> format.
/// Copies from an area in <see cref="ComponentOrder.Zyxw"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyFromZYXW(PixelArea<TColor> area, int targetY, int targetX, int width, int height)
protected virtual void CopyFromZyxw(PixelArea<TColor> area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
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
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.XYZ"/> format.
/// Copies from an area in <see cref="ComponentOrder.Xyz"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyFromXYZ(PixelArea<TColor> area, int targetY, int targetX, int width, int height)
protected virtual void CopyFromXyz(PixelArea<TColor> area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
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
}
/// <summary>
/// Copies from an area in <see cref="ComponentOrder.XYZW"/> format.
/// Copies from an area in <see cref="ComponentOrder.Xyzw"/> format.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyFromXYZW(PixelArea<TColor> area, int targetY, int targetX, int width, int height)
protected virtual void CopyFromXyzw(PixelArea<TColor> area, int targetX, int targetY, int width, int height)
{
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
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
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.ZYX"/> format.
/// Copies to an area in <see cref="ComponentOrder.Zyx"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyToZYX(PixelArea<TColor> area, int sourceY, int sourceX, int width, int height)
protected virtual void CopyToZyx(PixelArea<TColor> 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;
}
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.ZYXW"/> format.
/// Copies to an area in <see cref="ComponentOrder.Zyxw"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyToZYXW(PixelArea<TColor> area, int sourceY, int sourceX, int width, int height)
protected virtual void CopyToZyxw(PixelArea<TColor> 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;
}
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.XYZ"/> format.
/// Copies to an area in <see cref="ComponentOrder.Xyz"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyToXYZ(PixelArea<TColor> area, int sourceY, int sourceX, int width, int height)
protected virtual void CopyToXyz(PixelArea<TColor> 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;
}
}
}
/// <summary>
/// Copies to an area in <see cref="ComponentOrder.XYZW"/> format.
/// Copies to an area in <see cref="ComponentOrder.Xyzw"/> format.
/// </summary>
/// <param name="area">The row.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected virtual void CopyToXYZW(PixelArea<TColor> area, int sourceY, int sourceX, int width, int height)
protected virtual void CopyToXyzw(PixelArea<TColor> 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
}
/// <summary>
/// Checks that the given dimensions are within the bounds of the image.
/// Copy an area of pixels to the image.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="area">The area.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="width">The width of the area to copy.</param>
/// <param name="height">The height of the area to copy.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
private void CopyFrom(PixelArea<TColor> 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();
}
}
/// <summary>
/// Copy pixels from the image to an area of pixels.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="sourceX">The source column index.</param>
/// <param name="sourceY">The source row index.</param>
/// <param name="width">The width of the area to copy.</param>
/// <param name="height">The height of the area to copy.</param>
/// <exception cref="NotSupportedException">
/// Thrown when an unsupported component order value is passed.
/// </exception>
private void CopyTo(PixelArea<TColor> 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();
}
}
/// <summary>
/// Checks that the given area and offset are within the bounds of the image.
/// </summary>
/// <param name="area">The area.</param>
/// <param name="x">The x-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel.</param>
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the dimensions are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckDimensions(int width, int height)
private void CheckCoordinates(PixelArea<TColor> 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.");

104
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 <see cref="PixelArea{TColor}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
/// <param name="usePool">True if the buffer should be rented from ArrayPool</param>
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)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="width">The width. </param>
/// <param name="componentOrder">The component order.</param>
/// <param name="usePool">True if the buffer should be rented from ArrayPool</param>
public PixelArea(int width, ComponentOrder componentOrder, bool usePool = false)
: this(width, 1, componentOrder, 0, usePool)
/// <param name="padding">The number of bytes to pad each row.</param>
public PixelArea(int width, ComponentOrder componentOrder, int padding)
: this(width, 1, componentOrder, padding)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
/// </summary>
/// <param name="width">The width. </param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
/// <param name="padding">The number of bytes to pad each row.</param>
/// <param name="usePool">True if the buffer should be rented from ArrayPool</param>
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
/// <param name="height">The height.</param>
/// <param name="componentOrder">The component order.</param>
/// <param name="padding">The number of bytes to pad each row.</param>
/// <param name="usePool">True if the buffer should be rented from ArrayPool</param>
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
/// </summary>
public byte[] Bytes { get; }
/// <summary>
/// Gets the length of the buffer.
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the component order.
/// </summary>
@ -188,9 +180,9 @@ namespace ImageSharp
public byte* PixelBase { get; private set; }
/// <summary>
/// Gets number of bytes in a row.
/// Gets the width of one row in the number of bytes.
/// </summary>
public int RowByteCount { get; }
public int RowStride { get; }
/// <summary>
/// Gets the width.
@ -198,9 +190,9 @@ namespace ImageSharp
public int Width { get; }
/// <summary>
/// Gets the pool used to rent <see cref="Bytes"/>, 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.
/// </summary>
// ReSharper disable once StaticMemberInGenericType
// TODO: Use own pool?
private static ArrayPool<byte> BytesPool => ArrayPool<byte>.Shared;
/// <summary>
@ -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);
}
/// <summary>
@ -217,7 +216,7 @@ namespace ImageSharp
/// <param name="stream">The stream.</param>
public void Read(Stream stream)
{
stream.Read(this.Bytes, 0, this.Bytes.Length);
stream.Read(this.Bytes, 0, this.Length);
}
/// <summary>
@ -226,7 +225,7 @@ namespace ImageSharp
/// <param name="stream">The stream.</param>
public void Write(Stream stream)
{
stream.Write(this.Bytes, 0, this.Bytes.Length);
stream.Write(this.Bytes, 0, this.Length);
}
/// <summary>
@ -234,7 +233,7 @@ namespace ImageSharp
/// </summary>
internal void Reset()
{
Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowByteCount * this.Height));
Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowStride * this.Height));
}
/// <summary>
@ -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();
}
/// <summary>
/// Checks that the length of the byte array to ensure that it matches the given width and height.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="bytes">The byte array.</param>
/// <param name="componentOrder">The component order.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the byte array is th incorrect length.
/// </exception>
[Conditional("DEBUG")]
private void CheckBytesLength(int width, int height, byte[] bytes, ComponentOrder componentOrder)
{
@ -274,6 +283,10 @@ namespace ImageSharp
}
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
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);
}
}
}

28
src/ImageSharp/PixelAccessor.cs

@ -22,13 +22,13 @@ namespace ImageSharp
}
/// <inheritdoc />
protected override void CopyFromXYZW(PixelArea<Color> area, int targetY, int targetX, int width, int height)
protected override void CopyFromXyzw(PixelArea<Color> 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
}
/// <inheritdoc />
protected override void CopyFromXYZ(PixelArea<Color> area, int targetY, int targetX, int width, int height)
protected override void CopyFromXyz(PixelArea<Color> 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
}
/// <inheritdoc />
protected override void CopyFromZYX(PixelArea<Color> area, int targetY, int targetX, int width, int height)
protected override void CopyFromZyx(PixelArea<Color> 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
}
/// <inheritdoc />
protected override void CopyFromZYXW(PixelArea<Color> area, int targetY, int targetX, int width, int height)
protected override void CopyFromZyxw(PixelArea<Color> 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
}
/// <inheritdoc />
protected override void CopyToZYX(PixelArea<Color> area, int sourceY, int sourceX, int width, int height)
protected override void CopyToZyx(PixelArea<Color> 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
}
/// <inheritdoc />
protected override unsafe void CopyToXYZ(PixelArea<Color> area, int sourceY, int sourceX, int width, int height)
protected override void CopyToXyz(PixelArea<Color> 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
}
/// <inheritdoc />
protected override void CopyToZYXW(PixelArea<Color> area, int sourceY, int sourceX, int width, int height)
protected override void CopyToZyxw(PixelArea<Color> 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++)
{

50
src/ImageSharp/Profiles/Exif/ExifTag.cs

@ -29,15 +29,15 @@ namespace ImageSharp
/// <summary>
/// SubfileType
/// </summary>
[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,
/// <summary>
@ -276,15 +276,15 @@ namespace ImageSharp
/// <summary>
/// T4Options
/// </summary>
[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,
/// <summary>
/// T6Options
/// </summary>
[ExifTagDescription((uint)1, "Uncompressed")]
[ExifTagDescription(1U, "Uncompressed")]
T6Options = 0x0125,
/// <summary>
@ -383,9 +383,9 @@ namespace ImageSharp
/// <summary>
/// CleanFaxData
/// </summary>
[ExifTagDescription((uint)0, "Clean")]
[ExifTagDescription((uint)1, "Regenerated")]
[ExifTagDescription((uint)2, "Unclean")]
[ExifTagDescription(0U, "Clean")]
[ExifTagDescription(1U, "Regenerated")]
[ExifTagDescription(2U, "Unclean")]
CleanFaxData = 0x0147,
/// <summary>
@ -491,8 +491,8 @@ namespace ImageSharp
/// <summary>
/// ProfileType
/// </summary>
[ExifTagDescription((uint)0, "Unspecified")]
[ExifTagDescription((uint)1, "Group 3 FAX")]
[ExifTagDescription(0U, "Unspecified")]
[ExifTagDescription(1U, "Group 3 FAX")]
ProfileType = 0x0191,
/// <summary>
@ -512,13 +512,13 @@ namespace ImageSharp
/// <summary>
/// CodingMethods
/// </summary>
[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,
/// <summary>

14
src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs

@ -47,12 +47,7 @@ namespace ImageSharp.Quantizers
public override QuantizedImage<TColor> Quantize(ImageBase<TColor> 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
/// <summary>
/// Mask used when getting the appropriate pixels for a given node
/// </summary>
// ReSharper disable once StaticMemberInGenericType
private static readonly int[] Mask = { 0x100, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
/// <summary>
@ -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
/// <param name="buffer">The buffer array.</param>
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];

2
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;

15
src/ImageSharp/Quantizers/Wu/WuQuantizer.cs

@ -33,11 +33,6 @@ namespace ImageSharp.Quantizers
public sealed class WuQuantizer<TColor> : IQuantizer<TColor>
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 1e-5F;
/// <summary>
/// The index bits.
/// </summary>
@ -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);

4
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",

21
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<CoreColor> sourcePixels = source.Lock())
using (PixelAccessor<CoreColor> targetPixels = target.Lock())
{
Parallel.For(
0,
source.Height,
Bootstrapper.ParallelOptions,
y =>
{
sourcePixels.CopyBlock(0, y, targetPixels, 0, y, source.Width);
});
return targetPixels[0, 0];
}
}
}
}

2
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;
/// <summary>

165
tests/ImageSharp.Tests/Colors/PackedPixelTests.cs

@ -15,7 +15,7 @@ namespace ImageSharp.Tests.Colors
/// </summary>
/// <remarks>
/// The "ToVector4" tests should now be covered in <see cref="ColorConstructorTests"/>
/// and at some point they can be safely removed from here.
/// and at some point they can be safely removed from here.
/// </remarks>
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 });
}

18
tests/ImageSharp.Tests/Common/ConstantsTests.cs

@ -0,0 +1,18 @@
// <copyright file="ConstantsTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests.Common
{
using Xunit;
public class ConstantsTests
{
[Fact]
public void Epsilon()
{
Assert.Equal(Constants.Epsilon, 0.001f);
}
}
}

16
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);
}
}
}

6
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<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ);
PixelArea<TColor> area = new PixelArea<TColor>(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<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.XYZ);
PixelArea<TColor> area = new PixelArea<TColor>(8, 8, ComponentOrder.Xyz);
var dest = provider.Factory.CreateImage(8, 8);
using (var s = src.Lock())

8
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Tests
using ImageSharp.Formats;
using ImageSharp.Formats.Jpg;
/// <summary>
/// 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];

2
tests/ImageSharp.Tests/Formats/Jpg/UtilityTestClassBase.cs

@ -15,7 +15,7 @@ namespace ImageSharp.Tests
/// <summary>
/// Utility class to measure the execution of an operation.
/// </summary>
public class MeasureFixture
public class MeasureFixture : TestBase
{
protected bool EnablePrinting = true;

24
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<TColor>(TestImageProvider<TColor> provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
@ -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<TColor>(TestImageProvider<TColor> provider, ComponentOrder order)
where TColor : struct, IPackedPixel, IEquatable<TColor>
@ -181,7 +181,7 @@ namespace ImageSharp.Tests
byte blue = 3;
byte alpha = 255;
using (PixelArea<TColor> row = new PixelArea<TColor>(1, ComponentOrder.ZYX))
using (PixelArea<TColor> row = new PixelArea<TColor>(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<TColor> row = new PixelArea<TColor>(1, ComponentOrder.ZYXW))
using (PixelArea<TColor> row = new PixelArea<TColor>(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<TColor> row = new PixelArea<TColor>(1, ComponentOrder.ZYX))
using (PixelArea<TColor> row = new PixelArea<TColor>(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<TColor> row = new PixelArea<TColor>(1, ComponentOrder.ZYXW))
using (PixelArea<TColor> row = new PixelArea<TColor>(1, ComponentOrder.Zyxw))
{
pixels[0, 0] = (TColor)(object)new Color(red, green, blue, alpha);

12
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<PixelTypes, Type> PixelTypes2ClrTypes = new Dictionary<PixelTypes, Type>();
private static readonly PixelTypes[] AllConcretePixelTypes = EnumHelper.GetSortedValues<PixelTypes>()
.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<TColor>(this Image<TColor> a, Image<TColor> 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;

8
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<TColor>(
TestImageProvider<TColor> provider,
string message)
string message)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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<TColor>(
TestImageProvider<TColor> provider)
TestImageProvider<TColor> provider)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
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
/// <typeparam name="TColor"></typeparam>
/// <param name="factory"></param>
/// <returns></returns>
public static Image<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory)
public static Image<TColor> CreateTestImage<TColor>(GenericFactory<TColor> factory)
where TColor : struct, IPackedPixel, IEquatable<TColor>
{
return factory.CreateImage(3, 3);

7
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-*",

29
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());
}
}
}

179
tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ImageSharp.Tests</RootNamespace>
<AssemblyName>ImageSharp.Tests46</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="ImageSharp" Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<HintPath>..\..\src\ImageSharp\bin\Debug\net45\ImageSharp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="ImageSharp" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<HintPath>..\..\src\ImageSharp\bin\Release\net45\ImageSharp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Numerics.Vectors.4.1.1\lib\net46\System.Numerics.Vectors.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.assert, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.core, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.execution.desktop, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\Block8x8FTests.cs">
<Link>Formats\Jpg\Block8x8FTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\JpegTests.cs">
<Link>Formats\Jpg\JpegTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\ReferenceImplementations.cs">
<Link>Formats\Jpg\ReferenceImplementations.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\ReferenceImplementationsTests.cs">
<Link>Formats\Jpg\ReferenceImplementationsTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\UtilityTestClassBase.cs">
<Link>Formats\Jpg\UtilityTestClassBase.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Image\ImagePropertyTests.cs">
<Link>Image\ImagePropertyTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Image\ImageTests.cs">
<Link>Image\ImageTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Image\PixelAccessorTests.cs">
<Link>Image\PixelAccessorTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestBase.cs">
<Link>TestBase.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestImages.cs">
<Link>TestImages.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\ImageDataAttributeBase.cs">
<Link>TestUtilities\ImageDataAttributeBase.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithBlankImageAttribute.cs">
<Link>TestUtilities\WithBlankImageAttribute.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithFileAttribute.cs">
<Link>TestUtilities\WithFileAttribute.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithFileCollectionAttribute.cs">
<Link>TestUtilities\WithFileCollectionAttribute.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithMemberFactoryAttribute.cs">
<Link>TestUtilities\WithMemberFactoryAttribute.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithSolidFilledImagesAttribute.cs">
<Link>TestUtilities\WithSolidFilledImagesAttribute.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\EnumHelper.cs">
<Link>TestUtilities\EnumHelper.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Factories\GenericFactory.cs">
<Link>TestUtilities\GenericFactory.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Factories\ImageFactory.cs">
<Link>TestUtilities\ImageFactory.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\BlankProvider.cs">
<Link>TestUtilities\BlankProvider.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\FileProvider.cs">
<Link>TestUtilities\FileProvider.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\LambdaProvider.cs">
<Link>TestUtilities\LambdaProvider.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\SolidProvider.cs">
<Link>TestUtilities\SolidProvider.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\TestImageProvider.cs">
<Link>TestUtilities\TestImageProvider.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImagingTestCaseUtility.cs">
<Link>TestUtilities\ImagingTestCaseUtility.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\PixelTypes.cs">
<Link>TestUtilities\PixelTypes.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Tests\TestImageProviderTests.cs">
<Link>TestUtilities\TestImageProviderTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\Tests\TestUtilityExtensionsTests.cs">
<Link>TestUtilities\TestUtilityExtensionsTests.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\TestUtilities\TestUtilityExtensions.cs">
<Link>TestUtilities\TestUtilityExtensions.cs</Link>
</Compile>
<Compile Include="HelloTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestFile.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

36
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")]

78
tests/ImageSharp.Tests46/TestFile.cs

@ -0,0 +1,78 @@
// <copyright file="TestImage.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Concurrent;
using System.IO;
public class TestFile
{
private static readonly ConcurrentDictionary<string, TestFile> cache = new ConcurrentDictionary<string, TestFile>();
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);
}
}
}

10
tests/ImageSharp.Tests46/packages.config

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Numerics.Vectors" version="4.1.1" targetFramework="net461" />
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="net461" />
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
<package id="xunit.assert" version="2.2.0-beta4-build3444" targetFramework="net461" />
<package id="xunit.core" version="2.2.0-beta4-build3444" targetFramework="net461" />
<package id="xunit.extensibility.core" version="2.2.0-beta4-build3444" targetFramework="net461" />
<package id="xunit.extensibility.execution" version="2.2.0-beta4-build3444" targetFramework="net461" />
</packages>
Loading…
Cancel
Save