Browse Source

Merge branch 'master' of https://github.com/JimBobSquarePants/ImageSharp

Conflicts:
	src/ImageSharp/Formats/Jpg/Components/Block.cs
	src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs
	src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt
	src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
	src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs
	src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
	src/ImageSharp46/Formats/Jpg/Components/Bits.cs
	src/ImageSharp46/Formats/Jpg/Components/Block.cs
	src/ImageSharp46/Formats/Jpg/Components/Bytes.cs
	src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs
	src/ImageSharp46/ImageSharp46.csproj
	tests/ConsoleBenchmark/ConsoleBenchmark.csproj
	tests/ConsoleBenchmark/Program.cs
	tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
	tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
	tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj
	tests/ImageSharp.Tests46/packages.config
af/merge-core
Anton Firszov 10 years ago
parent
commit
773f98b3dd
  1. 34
      ImageSharp.46.sln
  2. 39
      README.md
  3. 32
      src/ImageSharp/Colors/Color.cs
  4. 2
      src/ImageSharp/Colors/ColorConstants.cs
  5. 2
      src/ImageSharp/Colors/ColorDefinitions.cs
  6. 225
      src/ImageSharp/Colors/ColorTransforms.cs
  7. 3
      src/ImageSharp/Colors/Colorspaces/CieLab.cs
  8. 153
      src/ImageSharp/Colors/PackedPixel/Alpha8.cs
  9. 170
      src/ImageSharp/Colors/PackedPixel/Bgr565.cs
  10. 164
      src/ImageSharp/Colors/PackedPixel/Bgra4444.cs
  11. 171
      src/ImageSharp/Colors/PackedPixel/Bgra5551.cs
  12. 177
      src/ImageSharp/Colors/PackedPixel/Byte4.cs
  13. 160
      src/ImageSharp/Colors/PackedPixel/HalfSingle.cs
  14. 145
      src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs
  15. 187
      src/ImageSharp/Colors/PackedPixel/HalfVector2.cs
  16. 182
      src/ImageSharp/Colors/PackedPixel/HalfVector4.cs
  17. 199
      src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs
  18. 197
      src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs
  19. 206
      src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
  20. 203
      src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
  21. 369
      src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs
  22. 3
      src/ImageSharp/Colors/PackedPixel/README.md
  23. 173
      src/ImageSharp/Colors/PackedPixel/Rg32.cs
  24. 172
      src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
  25. 170
      src/ImageSharp/Colors/PackedPixel/Rgba64.cs
  26. 200
      src/ImageSharp/Colors/PackedPixel/Short2.cs
  27. 216
      src/ImageSharp/Colors/PackedPixel/Short4.cs
  28. 44
      src/ImageSharp/Common/Extensions/ByteExtensions.cs
  29. 13
      src/ImageSharp/Common/Extensions/ComparableExtensions.cs
  30. 253
      src/ImageSharp/Common/Helpers/Vector4BlendTransforms.cs
  31. 2
      src/ImageSharp/Filters/Processors/ColorMatrix/GrayscaleBt709Processor.cs
  32. 222
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  33. 37
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  34. 73
      src/ImageSharp/Formats/Jpg/Components/Bits.cs
  35. 232
      src/ImageSharp/Formats/Jpg/Components/Block.cs
  36. 35
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs
  37. 45
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt
  38. 615
      src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs
  39. 141
      src/ImageSharp/Formats/Jpg/Components/Bytes.cs
  40. 41
      src/ImageSharp/Formats/Jpg/Components/Huffman.cs
  41. 2
      src/ImageSharp/Formats/Jpg/Components/IDCT.cs
  42. 46
      src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs
  43. 561
      src/ImageSharp/Formats/Jpg/JpegDecoderCore.cs
  44. 11
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  45. 29
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  46. 7
      src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
  47. 25
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  48. 26
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  49. 23
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  50. 523
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  51. 170
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  52. 2
      src/ImageSharp/Formats/Png/PngHeader.cs
  53. 23
      src/ImageSharp/Formats/Png/PngInterlaceMode.cs
  54. 10
      src/ImageSharp/IO/EndianBitConverter.cs
  55. 6
      src/ImageSharp/Image.cs
  56. 67
      src/ImageSharp/Image/Image.cs
  57. 65
      src/ImageSharp/Image/ImageFrame.cs
  58. 77
      src/ImageSharp/Image/PixelAccessor.cs
  59. 35
      src/ImageSharp/Image/PixelRow.cs
  60. 46
      src/ImageSharp/ImageFrame.cs
  61. 20
      src/ImageSharp/Numerics/Rectangle.cs
  62. 33
      src/ImageSharp/PixelAccessor.cs
  63. 9
      src/ImageSharp/Profiles/Exif/ExifProfile.cs
  64. 82
      src/ImageSharp/Profiles/Exif/ExifReader.cs
  65. 10
      src/ImageSharp/Profiles/Exif/ExifTagDescriptionAttribute.cs
  66. 292
      src/ImageSharp/Profiles/Exif/ExifValue.cs
  67. 55
      src/ImageSharp/Profiles/Exif/ExifWriter.cs
  68. 45
      src/ImageSharp/Quantizers/Wu/WuQuantizer.cs
  69. 5
      src/ImageSharp/Samplers/Processors/CropProcessor.cs
  70. 34
      src/ImageSharp/Samplers/Processors/RotateProcessor.cs
  71. 48
      src/ImageSharp/project.json
  72. 69
      src/ImageSharp46/Bootstrapper.cs
  73. 408
      src/ImageSharp46/Colors/Color.cs
  74. 179
      src/ImageSharp46/Colors/ColorConstants.cs
  75. 728
      src/ImageSharp46/Colors/ColorDefinitions.cs
  76. 74
      src/ImageSharp46/Colors/ColorTransforms.cs
  77. 292
      src/ImageSharp46/Colors/ColorspaceTransforms.cs
  78. 167
      src/ImageSharp46/Colors/Colorspaces/Bgra32.cs
  79. 192
      src/ImageSharp46/Colors/Colorspaces/CieLab.cs
  80. 184
      src/ImageSharp46/Colors/Colorspaces/CieXyz.cs
  81. 195
      src/ImageSharp46/Colors/Colorspaces/Cmyk.cs
  82. 213
      src/ImageSharp46/Colors/Colorspaces/Hsl.cs
  83. 206
      src/ImageSharp46/Colors/Colorspaces/Hsv.cs
  84. 30
      src/ImageSharp46/Colors/Colorspaces/IAlmostEquatable.cs
  85. 157
      src/ImageSharp46/Colors/Colorspaces/YCbCr.cs
  86. 33
      src/ImageSharp46/Colors/ComponentOrder.cs
  87. 31
      src/ImageSharp46/Colors/PackedPixel/IPackedBytes.cs
  88. 16
      src/ImageSharp46/Colors/PackedPixel/IPackedPixel.cs
  89. 42
      src/ImageSharp46/Colors/PackedPixel/IPackedVector.cs
  90. 33
      src/ImageSharp46/Colors/RgbaComponent.cs
  91. 45
      src/ImageSharp46/Common/Exceptions/ImageFormatException.cs
  92. 44
      src/ImageSharp46/Common/Exceptions/ImageProcessingException.cs
  93. 91
      src/ImageSharp46/Common/Extensions/ByteExtensions.cs
  94. 173
      src/ImageSharp46/Common/Extensions/ComparableExtensions.cs
  95. 88
      src/ImageSharp46/Common/Extensions/EnumerableExtensions.cs
  96. 30
      src/ImageSharp46/Common/Extensions/StreamExtensions.cs
  97. 83
      src/ImageSharp46/Common/Extensions/Vector4Extensions.cs
  98. 205
      src/ImageSharp46/Common/Helpers/Guard.cs
  99. 293
      src/ImageSharp46/Common/Helpers/ImageMaths.cs
  100. 48
      src/ImageSharp46/Filters/Alpha.cs

34
ImageSharp.46.sln

@ -1,34 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp46", "src\ImageSharp46\ImageSharp46.csproj", "{FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Tests46", "tests\ImageSharp.Tests46\ImageSharp.Tests46.csproj", "{635E0A15-3893-4763-A7F6-FCCFF85BCCA4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleBenchmark", "tests\ConsoleBenchmark\ConsoleBenchmark.csproj", "{8783E3A1-79F1-4E37-AA79-F06C48BED5E7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBA0B5F6-09C2-4317-8EF6-6ADB9B20E6B1}.Release|Any CPU.Build.0 = Release|Any CPU
{635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{635E0A15-3893-4763-A7F6-FCCFF85BCCA4}.Release|Any CPU.Build.0 = Release|Any CPU
{8783E3A1-79F1-4E37-AA79-F06C48BED5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8783E3A1-79F1-4E37-AA79-F06C48BED5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8783E3A1-79F1-4E37-AA79-F06C48BED5E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8783E3A1-79F1-4E37-AA79-F06C48BED5E7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

39
README.md

@ -5,8 +5,13 @@
> **ImageSharp is still in early stages (alpha) but progress has been pretty quick. As such, please do not use on production environments until the library reaches release candidate status. Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp).**
[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/APACHE-2.0-LICENSE.txt)
[![Build status](https://ci.appveyor.com/api/projects/status/hu6d1gdpxdw0q360/branch/master?svg=true)](https://ci.appveyor.com/project/JamesSouth/imagesharp/branch/master)
[![GitHub issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues)
[![GitHub stars](https://img.shields.io/github/stars/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/network)
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Twitter](https://img.shields.io/twitter/url/https/github.com/JimBobSquarePants/ImageSharp.svg?style=social)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fJimBobSquarePants%2fImageSharp&via=james_m_south)
### Installation
At present the code is pre-release but when ready it will be available on [Nuget](http://www.nuget.org).
@ -28,7 +33,7 @@ To clone it locally click the "Clone in Windows" button above or run the followi
git clone https://github.com/JimBobSquarePants/ImageSharp
```
###What works so far/ What is planned?
### What works so far/ What is planned?
- Encoding/decoding of image formats (plugable).
- [x] Jpeg (Includes Subsampling. Progressive writing required)
@ -43,16 +48,34 @@ git clone https://github.com/JimBobSquarePants/ImageSharp
- [x] Xiaolin Wu
- [x] Palette
- Basic color structs with implicit operators.
- [x] Color - 32bit color in RGBA order.
- [x] BGRA32
- [x] Color - 32bit color in RGBA order (IPackedPixel\<TPacked\>).
- [x] Bgra32
- [x] CIE Lab
- [x] CIE XYZ
- [x] CMYK
- [x] HSV
- [x] HSL
- [x] YCbCr
- Basic shape primitives (Vector backed)
- [x] Rectangle (Doesn't contain all System.Drawing methods)
- IPackedPixel\<TPacked\> representations of color models. Compatible with Microsoft XNA Game Studio and MonoGame.
- [x] Alpha8
- [x] Bgr565
- [x] Bgra444
- [x] Bgra565
- [x] Byte4
- [x] HalfSingle
- [x] HalfVector2
- [x] HalfVector4
- [x] NormalizedByte2
- [x] NormalizedByte4
- [x] NormalizedShort2
- [x] NormalizedShort4
- [x] Rg32
- [x] Rgba1010102
- [x] Rgba64
- [x] Short2
- [x] Short4
- Basic shape primitives.
- [x] Rectangle
- [x] Size
- [x] Point
- [x] Ellipse
@ -131,9 +154,9 @@ git clone https://github.com/JimBobSquarePants/ImageSharp
- [x] Threshold
- Drawing
- [ ] Path brush (Need help)
- [ ] Pattern brush (Need help)
- [ ] Hatch brush (Need help)
- [ ] Elliptical brush (Need help)
- [ ] Gradient brush (vignette? Need help)
- [ ] Gradient brush (Need help)
- Other stuff I haven't thought of.
### What might never happen
@ -193,4 +216,4 @@ Grand High Eternal Dictator
Core Team
- [Dirk Lemstra](https://github.com/dlemstra)
- [Jeavon Leopold](https://github.com/jeavon)
- [Jeavon Leopold](https://github.com/jeavon)

32
src/ImageSharp/Colors/Color.cs

@ -10,7 +10,7 @@ namespace ImageSharp
using System.Numerics;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
@ -19,9 +19,24 @@ namespace ImageSharp
/// </remarks>
public partial struct Color : IPackedPixel<uint>, IEquatable<Color>
{
/// <summary>
/// The shift count for the red component
/// </summary>
private const int RedShift = 0;
/// <summary>
/// The shift count for the green component
/// </summary>
private const int GreenShift = 8;
/// <summary>
/// The shift count for the blue component
/// </summary>
private const int BlueShift = 16;
/// <summary>
/// The shift count for the alpha component
/// </summary>
private const int AlphaShift = 24;
/// <summary>
@ -32,7 +47,7 @@ namespace ImageSharp
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5f);
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// The packed value.
@ -107,6 +122,17 @@ namespace ImageSharp
this.packedValue = Pack(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="packed">
/// The packed value.
/// </param>
public Color(uint packed)
{
this.packedValue = packed;
}
/// <summary>
/// Gets or sets the red component.
/// </summary>
@ -208,7 +234,7 @@ namespace ImageSharp
/// <param name="left">The <see cref="Color"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Color"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Color left, Color right)
{

2
src/ImageSharp/Colors/ColorConstants.cs

@ -26,7 +26,7 @@ namespace ImageSharp
/// <summary>
/// Returns an array of web safe colors.
/// </summary>
/// <returns></returns>
/// <returns>The <see cref="T:Color[]"/></returns>
private static Color[] GetWebSafeColors()
{
return new List<Color>

2
src/ImageSharp/Colors/ColorDefinitions.cs

@ -581,7 +581,7 @@ namespace ImageSharp
public static readonly Color Purple = new Color(128, 0, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #0.
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #663399.
/// </summary>
public static readonly Color RebeccaPurple = new Color(102, 51, 153, 255);

225
src/ImageSharp/Colors/ColorTransforms.cs

@ -5,7 +5,6 @@
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
@ -18,6 +17,48 @@ namespace ImageSharp
/// </remarks>
public partial struct Color
{
/// <summary>
/// Adds the second color to the first.
/// </summary>
/// <param name="left">The first source color.</param>
/// <param name="right">The second source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color operator +(Color left, Color right)
{
Vector4 add = left.ToVector4() + right.ToVector4();
return new Color(Pack(ref add));
}
/// <summary>
/// Subtracts the second color from the first.
/// </summary>
/// <param name="left">The first source color.</param>
/// <param name="right">The second source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color operator -(Color left, Color right)
{
Vector4 sub = left.ToVector4() - right.ToVector4();
return new Color(Pack(ref sub));
}
/// <summary>
/// The blending formula simply selects the source color.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Normal(Color backdrop, Color source)
{
Vector4 normal = Vector4BlendTransforms.Normal(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref normal));
}
/// <summary>
/// Blends two colors by multiplication.
/// <remarks>
@ -27,31 +68,171 @@ namespace ImageSharp
/// original color.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Multiply(Color backdrop, Color source)
{
Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref multiply));
}
/// <summary>
/// Multiplies the complements of the backdrop and source color values, then complements the result.
/// <remarks>
/// The result color is always at least as light as either of the two constituent colors. Screening any
/// color with white produces white; screening with black leaves the original color unchanged.
/// The effect is similar to projecting multiple photographic slides simultaneously onto a single screen.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Screen(Color backdrop, Color source)
{
Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref subtract));
}
/// <summary>
/// Multiplies or screens the colors, depending on the source color value. The effect is similar to
/// shining a harsh spotlight on the backdrop.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color HardLight(Color backdrop, Color source)
{
Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref hardlight));
}
/// <summary>
/// Multiplies or screens the colors, depending on the backdrop color value.
/// <remarks>
/// Source colors overlay the backdrop while preserving its highlights and shadows.
/// The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness
/// of the backdrop.
/// </remarks>
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Overlay(Color backdrop, Color source)
{
Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref overlay));
}
/// <summary>
/// Selects the darker of the backdrop and source colors.
/// The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Darken(Color backdrop, Color source)
{
Vector4 darken = Vector4BlendTransforms.Darken(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref darken));
}
/// <summary>
/// Selects the lighter of the backdrop and source colors.
/// The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Lighten(Color backdrop, Color source)
{
Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref lighten));
}
/// <summary>
/// Darkens or lightens the colors, depending on the source color value. The effect is similar to shining
/// a diffused spotlight on the backdrop.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color SoftLight(Color backdrop, Color source)
{
Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref softlight));
}
/// <summary>
/// Brightens the backdrop color to reflect the source color. Painting with black produces no changes.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color ColorDodge(Color backdrop, Color source)
{
Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref dodge));
}
/// <summary>
/// Darkens the backdrop color to reflect the source color. Painting with white produces no change.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color ColorBurn(Color backdrop, Color source)
{
Vector4 burn = Vector4BlendTransforms.Burn(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref burn));
}
/// <summary>
/// Subtracts the darker of the two constituent colors from the lighter color.
/// Painting with white inverts the backdrop color; painting with black produces no change.
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Difference(Color backdrop, Color source)
{
Vector4 difference = Vector4BlendTransforms.Difference(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref difference));
}
/// <summary>
/// Produces an effect similar to that of the <see cref="Difference"/> mode but lower in contrast. Painting with white
/// inverts the backdrop color; painting with black produces no change
/// </summary>
/// <param name="backdrop">The backdrop color.</param>
/// <param name="source">The source color.</param>
/// <param name="destination">The destination color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Multiply(Color source, Color destination)
public static Color Exclusion(Color backdrop, Color source)
{
if (destination == Color.Black)
{
return Color.Black;
}
if (destination == Color.White)
{
return source;
}
// TODO: This will use less memory than using Vector4
// but we should test speed vs memory to see which is best balance.
byte r = (byte)(source.R * destination.R).Clamp(0, 255);
byte g = (byte)(source.G * destination.G).Clamp(0, 255);
byte b = (byte)(source.B * destination.B).Clamp(0, 255);
byte a = (byte)(source.A * destination.A).Clamp(0, 255);
return new Color(r, g, b, a);
Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.ToVector4(), source.ToVector4());
return new Color(Pack(ref exclusion));
}
/// <summary>
@ -71,4 +252,4 @@ namespace ImageSharp
return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount));
}
}
}
}

3
src/ImageSharp/Colors/Colorspaces/CieLab.cs

@ -86,7 +86,8 @@ namespace ImageSharp
// Now to LAB
x /= 0.95047F;
//y /= 1F;
// y /= 1F;
z /= 1.08883F;
x = x > 0.008856F ? (float)Math.Pow(x, 0.3333333F) : ((903.3F * x) + 16F) / 116F;

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

@ -0,0 +1,153 @@
// <copyright file="Alpha8.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing a single 8 bit normalized W values that is ranging from 0 to 1.
/// </summary>
public struct Alpha8 : IPackedPixel<byte>, IEquatable<Alpha8>
{
/// <summary>
/// Initializes a new instance of the <see cref="Alpha8"/> struct.
/// </summary>
/// <param name="alpha">The alpha component</param>
public Alpha8(float alpha)
{
this.PackedValue = Pack(alpha);
}
/// <inheritdoc />
public byte PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Alpha8"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Alpha8"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Alpha8"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Alpha8 left, Alpha8 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Alpha8"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Alpha8"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Alpha8"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Alpha8 left, Alpha8 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(0, 0, 0, this.PackedValue / 255F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackedValue = w;
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Alpha8) && this.Equals((Alpha8)obj);
}
/// <summary>
/// Compares another Alpha8 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Alpha8 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Alpha8 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return (this.PackedValue / 255F).ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs a <see cref="float"/> into a byte.
/// </summary>
/// <param name="alpha">The float containing the value to pack.</param>
/// <returns>The <see cref="byte"/> containing the packed values.</returns>
private static byte Pack(float alpha)
{
return (byte)Math.Round(alpha.Clamp(0, 1) * 255F);
}
}
}

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

@ -0,0 +1,170 @@
// <copyright file="Bgr565.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Bgr565 : IPackedPixel<ushort>, IEquatable<Bgr565>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgr565"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
public Bgr565(float x, float y, float z)
{
this.PackedValue = Pack(x, y, z);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgr565"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed value.
/// </param>
public Bgr565(Vector3 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Bgr565"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgr565"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgr565"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgr565 left, Bgr565 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Bgr565"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgr565"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgr565"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgr565 left, Bgr565 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector3"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
public Vector3 ToVector3()
{
return new Vector3(
((this.PackedValue >> 11) & 0x1F) * (1F / 31F),
((this.PackedValue >> 5) & 0x3F) * (1F / 63F),
(this.PackedValue & 0x1F) * (1F / 31F));
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector3(), 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgr565) && this.Equals((Bgr565)obj);
}
/// <inheritdoc />
public bool Equals(Bgr565 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector3().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y, float z)
{
return (ushort)((((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 11) |
(((int)Math.Round(y.Clamp(0, 1) * 63F) & 0x3F) << 5) |
((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F));
}
}
}

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

@ -0,0 +1,164 @@
// <copyright file="Bgra4444.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w.
/// </summary>
public struct Bgra4444 : IPackedPixel<ushort>, IEquatable<Bgra4444>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgra4444"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Bgra4444(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra4444"/> struct.
/// </summary>
/// <param name="vector">The vector containing the components for the packed vector.</param>
public Bgra4444(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Bgra4444"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra4444"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra4444"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgra4444 left, Bgra4444 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Bgra4444"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra4444"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra4444"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgra4444 left, Bgra4444 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
const float Max = 1 / 15F;
return new Vector4(
((this.PackedValue >> 8) & 0x0F) * Max,
((this.PackedValue >> 4) & 0x0F) * Max,
(this.PackedValue & 0x0F) * Max,
((this.PackedValue >> 12) & 0x0F) * Max);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra4444) && this.Equals((Bgra4444)obj);
}
/// <inheritdoc />
public bool Equals(Bgra4444 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort)((((int)Math.Round(w.Clamp(0, 1) * 15F) & 0x0F) << 12) |
(((int)Math.Round(x.Clamp(0, 1) * 15F) & 0x0F) << 8) |
(((int)Math.Round(y.Clamp(0, 1) * 15F) & 0x0F) << 4) |
((int)Math.Round(z.Clamp(0, 1) * 15F) & 0x0F));
}
}
}

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

@ -0,0 +1,171 @@
// <copyright file="Bgra5551.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit.
/// </summary>
public struct Bgra5551 : IPackedPixel<ushort>, IEquatable<Bgra5551>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgra5551"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Bgra5551(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra5551"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
public Bgra5551(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Bgra5551"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra5551"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra5551"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgra5551 left, Bgra5551 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Bgra5551"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra5551"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra5551"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgra5551 left, Bgra5551 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
((this.PackedValue >> 10) & 0x1F) / 31F,
((this.PackedValue >> 5) & 0x1F) / 31F,
((this.PackedValue >> 0) & 0x1F) / 31F,
(this.PackedValue >> 15) & 0x01);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra5551) && this.Equals((Bgra5551)obj);
}
/// <inheritdoc />
public bool Equals(Bgra5551 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort)(
(((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 10) |
(((int)Math.Round(y.Clamp(0, 1) * 31F) & 0x1F) << 5) |
(((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0) |
(((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15));
}
}
}

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

@ -0,0 +1,177 @@
// <copyright file="Byte4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255.
/// </summary>
public struct Byte4 : IPackedPixel<uint>, IEquatable<Byte4>
{
/// <summary>
/// Initializes a new instance of the <see cref="Byte4"/> struct.
/// </summary>
/// <param name="vector">
/// A vector containing the initial values for the components of the Byte4 structure.
/// </param>
public Byte4(Vector4 vector)
{
this.PackedValue = Pack(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="Byte4"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Byte4(float x, float y, float z, float w)
{
Vector4 vector = new Vector4(x, y, z, w);
this.PackedValue = Pack(ref vector);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Byte4"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Byte4"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Byte4"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Byte4 left, Byte4 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Byte4"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Byte4"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Byte4"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Byte4 left, Byte4 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Sets the packed representation from a Vector4.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(ref vector);
}
/// <summary>
/// Expands the packed representation into a Vector4.
/// </summary>
/// <returns>The expanded vector.</returns>
public Vector4 ToVector4()
{
return new Vector4(
this.PackedValue & 0xFF,
(this.PackedValue >> 0x8) & 0xFF,
(this.PackedValue >> 0x10) & 0xFF,
(this.PackedValue >> 0x18) & 0xFF);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w));
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
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 override bool Equals(object obj)
{
return (obj is Byte4) && this.Equals((Byte4)obj);
}
/// <inheritdoc />
public bool Equals(Byte4 other)
{
return this == other;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return this.PackedValue.ToString("x8");
}
/// <summary>
/// Packs a vector into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(ref Vector4 vector)
{
const float Max = 255F;
const float Min = 0F;
// Clamp the value between min and max values
uint byte4 = (uint)Math.Round(vector.X.Clamp(Min, Max)) & 0xFF;
uint byte3 = ((uint)Math.Round(vector.Y.Clamp(Min, Max)) & 0xFF) << 0x8;
uint byte2 = ((uint)Math.Round(vector.Z.Clamp(Min, Max)) & 0xFF) << 0x10;
uint byte1 = ((uint)Math.Round(vector.W.Clamp(Min, Max)) & 0xFF) << 0x18;
return byte4 | byte3 | byte2 | byte1;
}
}
}

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

@ -0,0 +1,160 @@
// <copyright file="HalfSingle.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing a single 16 bit floating point value.
/// </summary>
public struct HalfSingle : IPackedPixel<ushort>, IEquatable<HalfSingle>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="HalfSingle"/> struct.
/// </summary>
/// <param name="single">The single component.</param>
public HalfSingle(float single)
{
this.PackedValue = HalfTypeHelper.Pack(single);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfSingle"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfSingle"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfSingle"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HalfSingle left, HalfSingle right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="HalfSingle"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfSingle"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfSingle"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HalfSingle left, HalfSingle right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="float"/>.
/// </summary>
/// <returns>The <see cref="float"/>.</returns>
public float ToSingle()
{
return HalfTypeHelper.Unpack(this.PackedValue);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = HalfTypeHelper.Pack(vector.X);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToSingle(), 0, 0, 1);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfSingle) && this.Equals((HalfSingle)obj);
}
/// <inheritdoc />
public bool Equals(HalfSingle other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToSingle().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
}
}

145
src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs

@ -0,0 +1,145 @@
// <copyright file="HalfTypeHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Runtime.InteropServices;
/// <summary>
/// Helper methods for packing and unpacking floating point values
/// </summary>
internal class HalfTypeHelper
{
/// <summary>
/// Packs a <see cref="float"/> into an <see cref="ushort"/>
/// </summary>
/// <param name="value">The float to pack</param>
/// <returns>The <see cref="ushort"/></returns>
internal static ushort Pack(float value)
{
Uif uif = new Uif { F = value };
return Pack(uif.I);
}
/// <summary>
/// Packs an <see cref="int"/> into a <see cref="ushort"/>
/// </summary>
/// <param name="value">The integer to pack.</param>
/// <returns>The <see cref="ushort"/></returns>
internal static ushort Pack(int value)
{
int s = (value >> 16) & 0x00008000;
int e = ((value >> 23) & 0x000000ff) - (127 - 15);
int m = value & 0x007fffff;
if (e <= 0)
{
if (e < -10)
{
return (ushort)s;
}
m = m | 0x00800000;
int t = 14 - e;
int a = (1 << (t - 1)) - 1;
int b = (m >> t) & 1;
m = (m + a + b) >> t;
return (ushort)(s | m);
}
if (e == 0xff - (127 - 15))
{
if (m == 0)
{
return (ushort)(s | 0x7c00);
}
m >>= 13;
return (ushort)(s | 0x7c00 | m | ((m == 0) ? 1 : 0));
}
m = m + 0x00000fff + ((m >> 13) & 1);
if ((m & 0x00800000) != 0)
{
m = 0;
e += 1;
}
if (e > 30)
{
return (ushort)(s | 0x7c00);
}
return (ushort)(s | (e << 10) | (m >> 13));
}
/// <summary>
/// Unpacks a <see cref="ushort"/> into a <see cref="float"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The <see cref="float"/>.</returns>
internal static float Unpack(ushort value)
{
uint result;
uint mantissa = (uint)(value & 1023);
uint exponent = 0xfffffff2;
if ((value & -33792) == 0)
{
if (mantissa != 0)
{
while ((mantissa & 1024) == 0)
{
exponent--;
mantissa = mantissa << 1;
}
mantissa &= 0xfffffbff;
result = ((uint)((((uint)value & 0x8000) << 16) | ((exponent + 127) << 23))) | (mantissa << 13);
}
else
{
result = (uint)((value & 0x8000) << 16);
}
}
else
{
result = ((((uint)value & 0x8000) << 16) | ((((((uint)value >> 10) & 0x1f) - 15) + 127) << 23)) | (mantissa << 13);
}
Uif uif = new Uif { U = result };
return uif.F;
}
/// <summary>
/// Maps the position of number types in memory
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct Uif
{
/// <summary>
/// The float.
/// </summary>
[FieldOffset(0)]
public float F;
/// <summary>
/// The integer.
/// </summary>
[FieldOffset(0)]
public int I;
/// <summary>
/// The unsigned integer.
/// </summary>
[FieldOffset(0)]
public uint U;
}
}
}

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

@ -0,0 +1,187 @@
// <copyright file="HalfVector2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit floating-point values.
/// </summary>
public struct HalfVector2 : IPackedPixel<uint>, IEquatable<HalfVector2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public HalfVector2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector2"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components.</param>
public HalfVector2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HalfVector2 left, HalfVector2 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HalfVector2 left, HalfVector2 right)
{
return !left.Equals(right);
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
Vector2 vector;
vector.X = HalfTypeHelper.Unpack((ushort)this.PackedValue);
vector.Y = HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10));
return vector;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
Vector2 vector = this.ToVector2();
return new Vector4(vector.X, vector.Y, 0F, 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector2().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfVector2) && this.Equals((HalfVector2)obj);
}
/// <inheritdoc />
public bool Equals(HalfVector2 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
uint num2 = HalfTypeHelper.Pack(x);
uint num = (uint)(HalfTypeHelper.Pack(y) << 0x10);
return num2 | num;
}
}
}

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

@ -0,0 +1,182 @@
// <copyright file="HalfVector4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit floating-point values.
/// </summary>
public struct HalfVector4 : IPackedPixel<ulong>, IEquatable<HalfVector4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public HalfVector4(float x, float y, float z, float w)
{
Vector4 vector = new Vector4(x, y, z, w);
this.PackedValue = Pack(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components</param>
public HalfVector4(Vector4 vector)
{
this.PackedValue = Pack(ref vector);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HalfVector4 left, HalfVector4 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HalfVector4 left, HalfVector4 right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(ref vector);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
HalfTypeHelper.Unpack((ushort)this.PackedValue),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30)));
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfVector4) && this.Equals((HalfVector4)obj);
}
/// <inheritdoc />
public bool Equals(HalfVector4 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <summary>
/// Packs a <see cref="Vector4"/> into a <see cref="ulong"/>.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(ref Vector4 vector)
{
ulong num4 = HalfTypeHelper.Pack(vector.X);
ulong num3 = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10;
ulong num2 = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20;
ulong num1 = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30;
return num4 | num3 | num2 | num1;
}
}
}

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

@ -0,0 +1,199 @@
// <copyright file="NormalizedByte2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedByte2 : IPackedPixel<ushort>, IEquatable<NormalizedByte2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte2"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedByte2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public NormalizedByte2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedByte2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="NormalizedByte2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
return new Vector2(
(sbyte)((this.PackedValue >> 0) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 8) & 0xFF) / 127F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector2(), 0F, 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedByte2) && this.Equals((NormalizedByte2)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedByte2 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y)
{
int byte2 = ((ushort)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0;
int byte1 = ((ushort)Math.Round(y.Clamp(-1F, 1F) * 127F) & 0xFF) << 8;
return (ushort)(byte2 | byte1);
}
}
}

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

@ -0,0 +1,197 @@
// <copyright file="NormalizedByte4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedByte4 : IPackedPixel<uint>, IEquatable<NormalizedByte4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte4"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedByte4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public NormalizedByte4(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedByte4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="NormalizedByte4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
(sbyte)((this.PackedValue >> 0) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 8) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 16) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 24) & 0xFF) / 127F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedByte4) && this.Equals((NormalizedByte4)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedByte4 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y, float z, float w)
{
uint byte4 = ((uint)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0;
uint byte3 = ((uint)Math.Round(y.Clamp(-1F, 1F) * 127F) & 0xFF) << 8;
uint byte2 = ((uint)Math.Round(z.Clamp(-1F, 1F) * 127F) & 0xFF) << 16;
uint byte1 = ((uint)Math.Round(w.Clamp(-1F, 1F) * 127F) & 0xFF) << 24;
return byte4 | byte3 | byte2 | byte1;
}
}
}

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

@ -0,0 +1,206 @@
// <copyright file="NormalizedShort2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedShort2 : IPackedPixel<uint>, IEquatable<NormalizedShort2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort2"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedShort2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public NormalizedShort2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedShort2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="NormalizedShort2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector2(), 0, 1);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
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();
}
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
const float MaxVal = 0x7FFF;
return new Vector2(
(short)(this.PackedValue & 0xFFFF) / MaxVal,
(short)(this.PackedValue >> 0x10) / MaxVal);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedShort2) && this.Equals((NormalizedShort2)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedShort2 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
const float MaxPos = 0x7FFF;
const float MinNeg = -MaxPos;
// Clamp the value between min and max values
// Round rather than truncate.
uint word2 = (uint)((int)(float)Math.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF);
uint word1 = (uint)(((int)(float)Math.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10);
return word2 | word1;
}
}
}

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

@ -0,0 +1,203 @@
// <copyright file="NormalizedShort4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedShort4 : IPackedPixel<ulong>, IEquatable<NormalizedShort4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort4"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedShort4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public NormalizedShort4(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedShort4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="NormalizedShort4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
const float MaxVal = 0x7FFF;
return new Vector4(
(short)((this.PackedValue >> 0x00) & 0xFFFF) / MaxVal,
(short)((this.PackedValue >> 0x10) & 0xFFFF) / MaxVal,
(short)((this.PackedValue >> 0x20) & 0xFFFF) / MaxVal,
(short)((this.PackedValue >> 0x30) & 0xFFFF) / MaxVal);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedShort4) && this.Equals((NormalizedShort4)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedShort4 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
const float MaxPos = 0x7FFF;
const float MinNeg = -MaxPos;
// Clamp the value between min and max values
ulong word4 = ((ulong)(float)Math.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x00;
ulong word3 = ((ulong)(float)Math.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10;
ulong word2 = ((ulong)(float)Math.Round(z * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x20;
ulong word1 = ((ulong)(float)Math.Round(w * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x30;
return word4 | word3 | word2 | word1;
}
}
}

369
src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs

@ -0,0 +1,369 @@
// <copyright file="PackedPixelConverterHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Assists with the conversion of known packed pixel formats from one to another.
/// </summary>
internal static class PackedPixelConverterHelper
{
/// <summary>
/// A non operative function. Simply returns the original vector.
/// </summary>
private static readonly Func<Vector4, Vector4> Noop = vector4 => vector4;
/// <summary>
/// Returns the correct scaling function for the given types The compute scale function.
/// </summary>
/// <param name="scaleFunc">The scale function.</param>
/// <typeparam name="TColor">The source pixel format.</typeparam>
/// <typeparam name="TColor2">The target pixel format.</typeparam>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
public static Func<Vector4, Vector4> ComputeScaleFunction<TColor, TColor2>(Func<Vector4, Vector4> scaleFunc)
{
// Custom type with a custom function.
if (scaleFunc != null)
{
return scaleFunc;
}
Type source = typeof(TColor);
Type target = typeof(TColor2);
// Normalized standard
if (IsStandardNormalizedType(source))
{
return FromStandardNormalizedType(target);
}
// Standard
if (IsStandardType(source))
{
return FromStandardType(target);
}
// Normalized offsets. All four components.
if (IsOffsetNormalizedType(source))
{
return FromOffsetNormalizedType(target);
}
// Offset. All four components.
if (IsOffsetType(source))
{
return FromOffsetType(target);
}
// Normalized offsets. First component pair only.
if (IsOffsetTwoComponentNormalizedType(source))
{
return FromOffsetTwoComponentNormalizedType(target);
}
// Offsets. First component pair only.
if (IsOffsetTwoComponentType(source))
{
return FromOffsetTwoComponentType(target);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromStandardNormalizedType(Type target)
{
if (IsStandardType(target))
{
return vector4 => 255F * vector4;
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range then offset the center down.
return vector4 => (2F * vector4) - Vector4.One;
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return vector4 => (65534F * vector4) - new Vector4(32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from 0 to 255.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromStandardType(Type target)
{
// Scale down
if (IsStandardNormalizedType(target))
{
return vector4 => vector4 / 255F;
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range, divide, then offset the center down.
return vector4 => ((2F * (vector4 / 255F)) - Vector4.One);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return vector4 => (65534F * (vector4 / 255F)) - new Vector4(32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -1 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 2F) + new Vector4(.5F);
}
if (IsStandardType(target))
{
// Compress the range, multiply, then offset the center up.
return vector4 => ((vector4 / 2F) * 255F) + new Vector4(127.5F);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
// Multiply out the range, two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -32767 to 32767.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 65534F) + new Vector4(.5F);
}
if (IsStandardType(target))
{
// Compress the range, multiply, then offset the center up.
return vector4 => ((vector4 / 65534F) * 255F) + new Vector4(127.5F);
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Compress the range. Two component won't read the last two values.
return vector4 => (vector4 / 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
// Compress the range then offset the center up for first pair.
Vector4 v = (vector4 / 2F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0F, 1F);
};
}
if (IsStandardType(target))
{
return vector4 =>
{
// Compress the range, multiply, then offset the center up for first pair.
Vector4 v = ((vector4 / 2F) * 255F) + new Vector4(127.5F);
return new Vector4(v.X, v.Y, 0F, 255F);
};
}
if (IsOffsetNormalizedType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -1F, 1F);
}
if (IsOffsetTwoComponentType(target))
{
// Multiply. Two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
if (IsOffsetType(target))
{
return vector4 =>
{
// Multiply the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 * 32767F;
return new Vector4(v.X, v.Y, -32767F, 32767F);
};
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
Vector4 v = (vector4 / 65534F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0, 1);
};
}
if (IsStandardType(target))
{
return vector4 =>
{
Vector4 v = ((vector4 / 65534F) * 255F) + new Vector4(127.5F);
return new Vector4(v.X, v.Y, 0, 255F);
};
}
if (IsOffsetType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -32767F, 32767F);
}
if (IsOffsetNormalizedType(target))
{
return vector4 =>
{
// Divide the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 / 32767F;
return new Vector4(v.X, v.Y, -1F, 1F);
};
}
if (IsOffsetTwoComponentNormalizedType(target))
{
// Divide. Two component won't read the last two values.
return vector4 => (vector4 / 32767F);
}
return Noop;
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardNormalizedType(Type type)
{
return type == typeof(Color)
|| type == typeof(Alpha8)
|| type == typeof(Bgr565)
|| type == typeof(Bgra4444)
|| type == typeof(Bgra5551)
|| type == typeof(HalfSingle)
|| type == typeof(HalfVector2)
|| type == typeof(HalfVector4)
|| type == typeof(Rg32)
|| type == typeof(Rgba1010102)
|| type == typeof(Rgba64);
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 255.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardType(Type type)
{
return type == typeof(Byte4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentNormalizedType(Type type)
{
return type == typeof(NormalizedByte2)
|| type == typeof(NormalizedShort2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -1 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetNormalizedType(Type type)
{
return type == typeof(NormalizedByte4)
|| type == typeof(NormalizedShort4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentType(Type type)
{
return type == typeof(Short2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -32767 to 32767.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetType(Type type)
{
return type == typeof(Short4);
}
}
}

3
src/ImageSharp/Colors/PackedPixel/README.md

@ -0,0 +1,3 @@
Pixel formats adapted and extended from:
https://github.com/MonoGame/MonoGame

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

@ -0,0 +1,173 @@
// <copyright file="Rg32.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rg32 : IPackedPixel<uint>, IEquatable<Rg32>, IPackedVector
{
/// <summary>
/// Initializes a new instance of the <see cref="Rg32"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
public Rg32(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rg32"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public Rg32(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rg32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rg32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rg32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rg32 left, Rg32 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rg32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rg32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rg32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rg32 left, Rg32 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
return new Vector2(
(this.PackedValue & 0xFFFF) / 65535F,
((this.PackedValue >> 16) & 0xFFFF) / 65535F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector2(), 0F, 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rg32) && this.Equals((Rg32)obj);
}
/// <inheritdoc />
public bool Equals(Rg32 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector2().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
return (uint)(
((int)Math.Round(x.Clamp(0, 1) * 65535F) & 0xFFFF) |
(((int)Math.Round(y.Clamp(0, 1) * 65535F) & 0xFFFF) << 16));
}
}
}

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

@ -0,0 +1,172 @@
// <copyright file="Rgba1010102.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x, y and z components use 10 bits, and the w component uses 2 bits.
/// </summary>
public struct Rgba1010102 : IPackedPixel<uint>, IEquatable<Rgba1010102>, IPackedVector
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba1010102"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Rgba1010102(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba1010102"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public Rgba1010102(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba1010102"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba1010102"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba1010102"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba1010102 left, Rgba1010102 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba1010102"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba1010102"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba1010102"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba1010102 left, Rgba1010102 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
((this.PackedValue >> 0) & 0x03FF) / 1023F,
((this.PackedValue >> 10) & 0x03FF) / 1023F,
((this.PackedValue >> 20) & 0x03FF) / 1023F,
((this.PackedValue >> 30) & 0x03) / 3F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rgba1010102) && this.Equals((Rgba1010102)obj);
}
/// <inheritdoc />
public bool Equals(Rgba1010102 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y, float z, float w)
{
return (uint)(
(((int)Math.Round(x.Clamp(0, 1) * 1023F) & 0x03FF) << 0) |
(((int)Math.Round(y.Clamp(0, 1) * 1023F) & 0x03FF) << 10) |
(((int)Math.Round(z.Clamp(0, 1) * 1023F) & 0x03FF) << 20) |
(((int)Math.Round(w.Clamp(0, 1) * 3F) & 0x03) << 30));
}
}
}

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

@ -0,0 +1,170 @@
// <copyright file="Rgba64.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rgba64 : IPackedPixel<ulong>, IEquatable<Rgba64>, IPackedVector
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Rgba64(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="vector">The vector containing the components values.</param>
public Rgba64(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba64"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba64"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba64 left, Rgba64 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba64"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba64"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba64 left, Rgba64 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
(this.PackedValue & 0xFFFF) / 65535F,
((this.PackedValue >> 16) & 0xFFFF) / 65535F,
((this.PackedValue >> 32) & 0xFFFF) / 65535F,
((this.PackedValue >> 48) & 0xFFFF) / 65535F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rgba64) && this.Equals((Rgba64)obj);
}
/// <inheritdoc />
public bool Equals(Rgba64 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
return (ulong)Math.Round(x.Clamp(0, 1) * 65535F) |
((ulong)Math.Round(y.Clamp(0, 1) * 65535F) << 16) |
((ulong)Math.Round(z.Clamp(0, 1) * 65535F) << 32) |
((ulong)Math.Round(w.Clamp(0, 1) * 65535F) << 48);
}
}
}

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

@ -0,0 +1,200 @@
// <copyright file="Short2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit signed integer values.
/// </summary>
public struct Short2 : IPackedPixel<uint>, IEquatable<Short2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector2 MaxBytes = new Vector2(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector2 Half = new Vector2(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector2 Round = new Vector2(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="Short2"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public Short2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="Short2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public Short2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Short2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Short2 left, Short2 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Short2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Short2 left, Short2 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector2 vector = new Vector2(x, y) / 255;
vector *= 65534;
vector -= new Vector2(32767);
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
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();
}
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
return new Vector2((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10));
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Short2) && this.Equals((Short2)obj);
}
/// <inheritdoc />
public bool Equals(Short2 other)
{
return this == other;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("x8");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
// Largest two byte positive number 0xFFFF >> 1;
const float MaxPos = 0x7FFF;
const float MinNeg = ~(int)MaxPos;
// Clamp the value between min and max values
uint word2 = (uint)Math.Round(x.Clamp(MinNeg, MaxPos)) & 0xFFFF;
uint word1 = ((uint)Math.Round(y.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x10;
return word2 | word1;
}
}
}

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

@ -0,0 +1,216 @@
// <copyright file="Short4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit signed integer values.
/// </summary>
public struct Short4 : IPackedPixel<ulong>, IEquatable<Short4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="Short4"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components.</param>
public Short4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the <see cref="Short4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public Short4(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Short4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Short4 left, Short4 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Short4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Short4 left, Short4 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
(short)(this.PackedValue & 0xFFFF),
(short)((this.PackedValue >> 0x10) & 0xFFFF),
(short)((this.PackedValue >> 0x20) & 0xFFFF),
(short)((this.PackedValue >> 0x30) & 0xFFFF));
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w) / 255;
vector *= 65534;
vector -= new Vector4(32767);
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
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();
}
}
/// <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>
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>
public bool Equals(Short4 other)
{
return this == other;
}
/// <summary>
/// Gets the hash code for the current instance.
/// </summary>
/// <returns>Hash code for the instance.</returns>
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return this.PackedValue.ToString("x16");
}
/// <summary>
/// Packs the components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
// Largest two byte positive number 0xFFFF >> 1;
const float MaxPos = 0x7FFF;
// Two's complement
const float MinNeg = ~(int)MaxPos;
// Clamp the value between min and max values
ulong word4 = ((ulong)Math.Round(x.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x00;
ulong word3 = ((ulong)Math.Round(y.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x10;
ulong word2 = ((ulong)Math.Round(z.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x20;
ulong word1 = ((ulong)Math.Round(w.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x30;
return word4 | word3 | word2 | word1;
}
}
}

44
src/ImageSharp/Common/Extensions/ByteExtensions.cs

@ -12,50 +12,6 @@ namespace ImageSharp
/// </summary>
internal static class ByteExtensions
{
/// <summary>
/// Converts a byte array to a new array where each value in the original array is represented
/// by a the specified number of bits.
/// </summary>
/// <param name="source">The bytes to convert from. Cannot be null.</param>
/// <param name="bits">The number of bits per value.</param>
/// <returns>The resulting <see cref="T:byte[]"/> array. Is never null.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
public static byte[] ToArrayByBitsLength(this byte[] source, int bits)
{
Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThan(bits, 0, "bits");
byte[] result;
if (bits < 8)
{
result = new byte[source.Length * 8 / bits];
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < source.Length; i++)
{
byte b = source[i];
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
result[resultOffset] = (byte)colorIndex;
resultOffset++;
}
}
}
else
{
result = source;
}
return result;
}
/// <summary>
/// Optimized <see cref="T:byte[]"/> reversal algorithm.
/// </summary>

13
src/ImageSharp/Common/Extensions/ComparableExtensions.cs

@ -165,5 +165,18 @@ namespace ImageSharp
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Swaps the references to two objects in memory.
/// </summary>
/// <param name="first">The first reference.</param>
/// <param name="second">The second reference.</param>
/// <typeparam name="T">The type of object.</typeparam>
public static void Swap<T>(ref T first, ref T second)
{
T temp = second;
second = first;
first = temp;
}
}
}

253
src/ImageSharp/Common/Helpers/Vector4BlendTransforms.cs

@ -0,0 +1,253 @@
// <copyright file="Vector4BlendTransforms.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Transform algorithms that match the equations defined in the W3C Compositing and Blending Level 1 specification.
/// <see href="https://www.w3.org/TR/compositing-1/"/>
/// </summary>
public class Vector4BlendTransforms
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001F;
/// <summary>
/// The blending formula simply selects the source vector.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Normal(Vector4 backdrop, Vector4 source)
{
return new Vector4(source.X, source.Y, source.Z, source.W);
}
/// <summary>
/// Blends two vectors by multiplication.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Multiply(Vector4 backdrop, Vector4 source)
{
Vector4 multiply = backdrop * source;
multiply.W = backdrop.W;
return multiply;
}
/// <summary>
/// Multiplies the complements of the backdrop and source vector values, then complements the result.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Screen(Vector4 backdrop, Vector4 source)
{
Vector4 subtract = backdrop + source - (backdrop * source);
subtract.W = backdrop.W;
return subtract;
}
/// <summary>
/// Multiplies or screens the colors, depending on the source vector value.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 HardLight(Vector4 backdrop, Vector4 source)
{
return new Vector4(BlendOverlay(source.X, backdrop.X), BlendOverlay(source.Y, backdrop.Y), BlendOverlay(source.Z, backdrop.Z), backdrop.W);
}
/// <summary>
/// Multiplies or screens the vectors, depending on the backdrop vector value.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Overlay(Vector4 backdrop, Vector4 source)
{
return new Vector4(BlendOverlay(backdrop.X, source.X), BlendOverlay(backdrop.Y, source.Y), BlendOverlay(backdrop.Z, source.Z), backdrop.W);
}
/// <summary>
/// Selects the minimum of the backdrop and source vectors.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Darken(Vector4 backdrop, Vector4 source)
{
Vector4 result = Vector4.Min(backdrop, source);
result.W = backdrop.W;
return result;
}
/// <summary>
/// Selects the max of the backdrop and source vector.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Lighten(Vector4 backdrop, Vector4 source)
{
Vector4 result = Vector4.Max(backdrop, source);
result.W = backdrop.W;
return result;
}
/// <summary>
/// Selects the maximum or minimum of the vectors, depending on the source vector value.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 SoftLight(Vector4 backdrop, Vector4 source)
{
return new Vector4(BlendSoftLight(backdrop.X, source.X), BlendSoftLight(backdrop.Y, source.Y), BlendSoftLight(backdrop.Z, source.Z), backdrop.W);
}
/// <summary>
/// Increases the backdrop vector to reflect the source vector.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Dodge(Vector4 backdrop, Vector4 source)
{
return new Vector4(BlendDodge(backdrop.X, source.X), BlendDodge(backdrop.Y, source.Y), BlendDodge(backdrop.Z, source.Z), backdrop.W);
}
/// <summary>
/// Decreases the backdrop vector to reflect the source vector.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Burn(Vector4 backdrop, Vector4 source)
{
return new Vector4(BlendBurn(backdrop.X, source.X), BlendBurn(backdrop.Y, source.Y), BlendBurn(backdrop.Z, source.Z), backdrop.W);
}
/// <summary>
/// Subtracts the minimum of the two constituent vectors from the maximum vector.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Difference(Vector4 backdrop, Vector4 source)
{
Vector4 result = Vector4.Abs(backdrop - source);
result.W = backdrop.W;
return result;
}
/// <summary>
/// Produces an effect similar to that of the <see cref="Difference"/> mode but lower in magnitude.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <returns>
/// The <see cref="Vector4"/>.
/// </returns>
public static Vector4 Exclusion(Vector4 backdrop, Vector4 source)
{
return new Vector4(BlendExclusion(backdrop.X, source.X), BlendExclusion(backdrop.Y, source.Y), BlendExclusion(backdrop.Z, source.Z), backdrop.W);
}
/// <summary>
/// Multiplies or screens the color component, depending on the component value.
/// </summary>
/// <param name="b">The backdrop component.</param>
/// <param name="s">The source component.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float BlendOverlay(float b, float s)
{
return b <= .5F ? (2F * b * s) : (1F - (2F * (1F - b) * (1F - s)));
}
/// <summary>
/// Darkens or lightens the backdrop component, depending on the source component value.
/// </summary>
/// <param name="b">The backdrop component.</param>
/// <param name="s">The source component.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float BlendSoftLight(float b, float s)
{
return s <= .5F ? ((2F * b * s) + (b * b * (1F - (2F * s)))) : (float)((Math.Sqrt(b) * ((2F * s) - 1F)) + (2F * b * (1F - s)));
}
/// <summary>
/// Brightens the backdrop component to reflect the source component.
/// </summary>
/// <param name="b">The backdrop component.</param>
/// <param name="s">The source component.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float BlendDodge(float b, float s)
{
return Math.Abs(s - 1F) < Epsilon ? s : Math.Min(b / (1F - s), 1F);
}
/// <summary>
/// Darkens the backdrop component to reflect the source component.
/// </summary>
/// <param name="b">The backdrop component.</param>
/// <param name="s">The source component.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float BlendBurn(float b, float s)
{
return Math.Abs(s) < Epsilon ? s : Math.Max(1F - ((1F - b) / s), 0F);
}
/// <summary>
/// Darkens the backdrop component to reflect the source component.
/// </summary>
/// <param name="b">The backdrop component.</param>
/// <param name="s">The source component.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float BlendExclusion(float b, float s)
{
return b + s - (2F * b * s);
}
}
}

2
src/ImageSharp/Filters/Processors/ColorMatrix/GrayscaleBt709Processor.cs

@ -11,6 +11,8 @@ namespace ImageSharp.Processors
/// Converts the colors of the image to Grayscale applying the formula as specified by
/// ITU-R Recommendation BT.709 <see href="https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class GrayscaleBt709Processor<TColor, TPacked> : ColorMatrixFilter<TColor, TPacked>
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct

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

@ -33,9 +33,14 @@ namespace ImageSharp.Formats
private byte[] globalColorTable;
/// <summary>
/// The current frame.
/// The next frame.
/// </summary>
private TColor[] currentFrame;
private ImageFrame<TColor, TPacked> nextFrame;
/// <summary>
/// The area to restore.
/// </summary>
private Rectangle? restoreArea;
/// <summary>
/// The logical screen descriptor.
@ -285,141 +290,174 @@ namespace ImageSharp.Formats
/// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor)
private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor)
{
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
if (this.currentFrame == null)
ImageFrame<TColor, TPacked> previousFrame = null;
ImageFrame<TColor, TPacked> currentFrame = null;
ImageBase<TColor, TPacked> image;
if (this.nextFrame == null)
{
this.currentFrame = new TColor[imageWidth * imageHeight];
}
image = this.decodedImage;
TColor[] lastFrame = null;
image.Quality = colorTable.Length / 3;
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
// This initializes the image to become fully transparent because the alpha channel is zero.
image.InitPixels(imageWidth, imageHeight);
}
else
{
lastFrame = new TColor[imageWidth * imageHeight];
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
previousFrame = this.nextFrame;
}
currentFrame = this.nextFrame.Clone();
image = currentFrame;
Array.Copy(this.currentFrame, lastFrame, lastFrame.Length);
this.RestoreToBackground(image);
this.decodedImage.Frames.Add(currentFrame);
}
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
{
image.FrameDelay = this.graphicsControlExtension.DelayTime;
}
int offset, i = 0;
int i = 0;
int interlacePass = 0; // The interlace pass
int interlaceIncrement = 8; // The interlacing line increment
int interlaceY = 0; // The current interlaced line
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
using (PixelAccessor<TColor, TPacked> pixelAccessor = image.Lock())
{
// Check if this image is interlaced.
int writeY; // the target y offset to write to
if (descriptor.InterlaceFlag)
using (PixelRow<TColor, TPacked> pixelRow = new PixelRow<TColor, TPacked>(imageWidth, ComponentOrder.XYZW))
{
// If so then we read lines at predetermined offsets.
// When an entire image height worth of offset lines has been read we consider this a pass.
// With each pass the number of offset lines changes and the starting line changes.
if (interlaceY >= descriptor.Height)
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
{
interlacePass++;
switch (interlacePass)
// Check if this image is interlaced.
int writeY; // the target y offset to write to
if (descriptor.InterlaceFlag)
{
case 1:
interlaceY = 4;
break;
case 2:
interlaceY = 2;
interlaceIncrement = 4;
break;
case 3:
interlaceY = 1;
interlaceIncrement = 2;
break;
// If so then we read lines at predetermined offsets.
// When an entire image height worth of offset lines has been read we consider this a pass.
// With each pass the number of offset lines changes and the starting line changes.
if (interlaceY >= descriptor.Height)
{
interlacePass++;
switch (interlacePass)
{
case 1:
interlaceY = 4;
break;
case 2:
interlaceY = 2;
interlaceIncrement = 4;
break;
case 3:
interlaceY = 1;
interlaceIncrement = 2;
break;
}
}
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement;
}
else
{
writeY = y;
}
}
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement;
}
else
{
writeY = y;
}
pixelRow.Reset();
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
{
offset = (writeY * imageWidth) + x;
int index = indices[i];
byte* pixelBase = pixelRow.PixelBase;
for (int x = 0; x < descriptor.Width; x++)
{
int index = indices[i];
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
int indexOffset = index * 3;
*(pixelBase + 0) = colorTable[indexOffset];
*(pixelBase + 1) = colorTable[indexOffset + 1];
*(pixelBase + 2) = colorTable[indexOffset + 2];
*(pixelBase + 3) = 255;
}
i++;
pixelBase += 4;
}
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
this.graphicsControlExtension.TransparencyIndex != index)
{
// Stored in r-> g-> b-> a order.
int indexOffset = index * 3;
TColor pixel = default(TColor);
pixel.PackFromVector4(new Color(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2]).ToVector4());
this.currentFrame[offset] = pixel;
pixelAccessor.CopyFrom(pixelRow, writeY, descriptor.Left);
}
i++;
}
}
TColor[] pixels = new TColor[imageWidth * imageHeight];
Array.Copy(this.currentFrame, pixels, pixels.Length);
ImageBase<TColor, TPacked> currentImage;
if (this.decodedImage.Pixels == null)
if (previousFrame != null)
{
currentImage = this.decodedImage;
currentImage.SetPixels(imageWidth, imageHeight, pixels);
currentImage.Quality = colorTable.Length / 3;
this.nextFrame = previousFrame;
return;
}
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
{
this.decodedImage.FrameDelay = this.graphicsControlExtension.DelayTime;
}
if (currentFrame == null)
{
this.nextFrame = this.decodedImage.ToFrame();
}
else
{
ImageFrame<TColor, TPacked> frame = new ImageFrame<TColor, TPacked>();
this.nextFrame = currentFrame.Clone();
}
if (this.graphicsControlExtension != null &&
this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
{
this.restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height);
}
}
currentImage = frame;
currentImage.SetPixels(imageWidth, imageHeight, pixels);
currentImage.Quality = colorTable.Length / 3;
private void RestoreToBackground(ImageBase<TColor, TPacked> frame)
{
if (this.restoreArea == null)
{
return;
}
if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0)
// Optimization for when the size of the frame is the same as the image size.
if (this.restoreArea.Value.Width == this.decodedImage.Width &&
this.restoreArea.Value.Height == this.decodedImage.Height)
{
using (PixelAccessor<TColor, TPacked> pixelAccessor = frame.Lock())
{
currentImage.FrameDelay = this.graphicsControlExtension.DelayTime;
pixelAccessor.Reset();
}
this.decodedImage.Frames.Add(frame);
}
if (this.graphicsControlExtension != null)
else
{
if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground)
using (PixelRow<TColor, TPacked> emptyRow = new PixelRow<TColor, TPacked>(this.restoreArea.Value.Width, ComponentOrder.XYZW))
{
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
using (PixelAccessor<TColor, TPacked> pixelAccessor = frame.Lock())
{
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++)
for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++)
{
offset = (y * imageWidth) + x;
// Stored in r-> g-> b-> a order.
this.currentFrame[offset] = default(TColor);
pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left);
}
}
}
else if (this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToPrevious)
{
this.currentFrame = lastFrame;
}
}
this.restoreArea = null;
}
}
}

37
src/ImageSharp/Formats/Gif/LzwDecoder.cs

@ -90,7 +90,7 @@ namespace ImageSharp.Formats
suffix[code] = (byte)code;
}
byte[] buffer = null;
byte[] buffer = new byte[255];
while (xyz < pixels.Length)
{
if (top == 0)
@ -101,8 +101,7 @@ namespace ImageSharp.Formats
if (count == 0)
{
// Read a new data block.
buffer = this.ReadBlock();
count = buffer.Length;
count = this.ReadBlock(buffer);
if (count == 0)
{
break;
@ -111,10 +110,7 @@ namespace ImageSharp.Formats
bi = 0;
}
if (buffer != null)
{
data += buffer[bi] << bits;
}
data += buffer[bi] << bits;
bits += 8;
bi++;
@ -200,29 +196,20 @@ namespace ImageSharp.Formats
/// Reads the next data block from the stream. A data block begins with a byte,
/// which defines the size of the block, followed by the block itself.
/// </summary>
/// <param name="buffer">The buffer to store the block in.</param>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private byte[] ReadBlock()
private int ReadBlock(byte[] buffer)
{
int blockSize = this.stream.ReadByte();
return this.ReadBytes(blockSize);
}
int bufferSize = this.stream.ReadByte();
if (bufferSize < 1)
{
return 0;
}
/// <summary>
/// Reads the specified number of bytes from the data stream.
/// </summary>
/// <param name="length">
/// The number of bytes to read.
/// </param>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
private byte[] ReadBytes(int length)
{
byte[] buffer = new byte[length];
this.stream.Read(buffer, 0, length);
return buffer;
int count = this.stream.Read(buffer, 0, bufferSize);
return count != bufferSize ? 0 : bufferSize;
}
}
}

73
src/ImageSharp/Formats/Jpg/Components/Bits.cs

@ -5,27 +5,92 @@
namespace ImageSharp.Formats
{
using System.Runtime.CompilerServices;
/// <summary>
/// Holds the unprocessed bits that have been taken from the byte-stream.
/// The n least significant bits of a form the unread bits, to be read in MSB to
/// LSB order.
/// </summary>
internal class Bits
internal struct Bits
{
/// <summary>
/// Gets or sets the accumulator.
/// </summary>
public uint Accumulator { get; set; }
public uint Accumulator;
/// <summary>
/// Gets or sets the mask.
/// <![CDATA[mask==1<<(unreadbits-1) when unreadbits>0, with mask==0 when unreadbits==0.]]>
/// </summary>
public uint Mask { get; set; }
public uint Mask;
/// <summary>
/// Gets or sets the number of unread bits in the accumulator.
/// </summary>
public int UnreadBits { get; set; }
public int UnreadBits;
/// <summary>
/// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at
/// least n. For best performance (avoiding function calls inside hot loops),
/// the caller is the one responsible for first checking that bits.UnreadBits &lt; n.
/// </summary>
/// <param name="n">The number of bits to ensure.</param>
/// <param name="decoder"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal JpegDecoderCore.ErrorCodes EnsureNBits(int n, JpegDecoderCore decoder)
{
while (true)
{
JpegDecoderCore.ErrorCodes errorCode;
byte c = decoder.bytes.ReadByteStuffedByte(decoder.inputStream, out errorCode);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
{
return errorCode;
}
this.Accumulator = (this.Accumulator << 8) | c;
this.UnreadBits += 8;
if (this.Mask == 0)
{
this.Mask = 1 << 7;
}
else
{
this.Mask <<= 8;
}
if (this.UnreadBits >= n)
{
return JpegDecoderCore.ErrorCodes.NoError;
}
}
}
internal int ReceiveExtend(byte t, JpegDecoderCore decoder)
{
if (this.UnreadBits < t)
{
var errorCode = this.EnsureNBits(t, decoder);
if (errorCode != JpegDecoderCore.ErrorCodes.NoError)
{
throw new JpegDecoderCore.MissingFF00Exception();
}
}
this.UnreadBits -= t;
this.Mask >>= t;
int s = 1 << t;
int x = (int)((this.Accumulator >> this.UnreadBits) & (s - 1));
if (x < (s >> 1))
{
x += ((-1) << t) + 1;
}
return x;
}
}
}

232
src/ImageSharp/Formats/Jpg/Components/Block.cs

@ -3,113 +3,142 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
namespace ImageSharp.Formats
{
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
/// <summary>
/// Represents an 8x8 block of coefficients to transform and encode.
/// </summary>
internal struct Block : IDisposable
{
private static readonly ArrayPool<int> ArrayPool = ArrayPool<int>.Create(BlockSize, 50);
/// <summary>
/// Gets the size of the block.
/// </summary>
public const int BlockSize = 64;
/// <summary>
/// The array of block data.
/// Gets the array of block data.
/// </summary>
public int[] Data;
/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// A pool of reusable buffers.
/// </summary>
//public Block()
//{
// this.data = new int[BlockSize];
//}
private static readonly ArrayPool<int> ArrayPool = ArrayPool<int>.Create(BlockSize, 50);
public void Init()
/// <summary>
/// Gets a value indicating whether the block is initialized
/// </summary>
public bool IsInitialized => this.Data != null;
/// <summary>
/// Gets the pixel data at the given block index.
/// </summary>
/// <param name="index">The index of the data to return.</param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int this[int index]
{
//this.Data = new int[BlockSize];
this.Data = ArrayPool.Rent(BlockSize);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Data[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.Data[index] = value;
}
}
/// <summary>
/// Creates a new block
/// </summary>
/// <returns>The <see cref="Block"/></returns>
public static Block Create()
{
var block = new Block();
Block block = default(Block);
block.Init();
return block;
}
public static Block[] CreateArray(int size)
/// <summary>
/// Returns an array of blocks of the given length.
/// </summary>
/// <param name="count">The number to create.</param>
/// <returns>The <see cref="T:Block[]"/></returns>
public static Block[] CreateArray(int count)
{
Block[] result = new Block[size];
Block[] result = new Block[count];
for (int i = 0; i < result.Length; i++)
{
result[i].Init();
}
return result;
}
public bool IsInitialized => this.Data != null;
/// <summary>
/// Gets the pixel data at the given block index.
/// Disposes of the collection of blocks
/// </summary>
/// <param name="index">The index of the data to return.</param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
public int this[int index]
/// <param name="blocks">The blocks.</param>
public static void DisposeAll(Block[] blocks)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return this.Data[index]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set { this.Data[index] = value; }
for (int i = 0; i < blocks.Length; i++)
{
blocks[i].Dispose();
}
}
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
public void Dispose()
/// <summary>
/// Initializes the new block.
/// </summary>
public void Init()
{
if (Data != null)
{
ArrayPool.Return(Data, true);
Data = null;
}
this.Data = ArrayPool.Rent(BlockSize);
}
public static void DisposeAll(Block[] blocks)
/// <inheritdoc />
public void Dispose()
{
for (int i = 0; i < blocks.Length; i++)
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
if (this.Data != null)
{
blocks[i].Dispose();
ArrayPool.Return(this.Data, true);
this.Data = null;
}
}
/// <summary>
/// Clears the block data
/// </summary>
public void Clear()
{
for (int i = 0; i < Data.Length; i++)
for (int i = 0; i < this.Data.Length; i++)
{
Data[i] = 0;
this.Data[i] = 0;
}
}
/// <summary>
/// Clones the current block
/// </summary>
/// <returns>The <see cref="Block"/></returns>
public Block Clone()
{
Block clone = Create();
Array.Copy(Data, clone.Data, BlockSize);
Array.Copy(this.Data, clone.Data, BlockSize);
return clone;
}
}
/// <summary>
/// TODO: Should be removed, when JpegEncoderCore is refactored to use Block8x8F
/// Temporal class to make refactoring easier.
/// 1. Refactor Block -> BlockF
/// 2. Test
@ -117,10 +146,8 @@ namespace ImageSharp.Formats
/// </summary>
internal struct BlockF : IDisposable
{
private static readonly ArrayPool<float> ArrayPool = ArrayPool<float>.Create(BlockSize, 50);
/// <summary>
/// Gets the size of the block.
/// Size of the block.
/// </summary>
public const int BlockSize = 64;
@ -130,36 +157,13 @@ namespace ImageSharp.Formats
public float[] Data;
/// <summary>
/// Initializes a new instance of the <see cref="Block"/> class.
/// A pool of reusable buffers.
/// </summary>
//public Block()
//{
// this.data = new int[BlockSize];
//}
public void Init()
{
//this.Data = new int[BlockSize];
this.Data = ArrayPool.Rent(BlockSize);
}
public static BlockF Create()
{
var block = new BlockF();
block.Init();
return block;
}
public static BlockF[] CreateArray(int size)
{
BlockF[] result = new BlockF[size];
for (int i = 0; i < result.Length; i++)
{
result[i].Init();
}
return result;
}
private static readonly ArrayPool<float> ArrayPool = ArrayPool<float>.Create(BlockSize, 50);
/// <summary>
/// Gets a value indicating whether the block is initialized
/// </summary>
public bool IsInitialized => this.Data != null;
/// <summary>
@ -172,21 +176,49 @@ namespace ImageSharp.Formats
public float this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return this.Data[index]; }
get
{
return this.Data[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set { this.Data[index] = value; }
set
{
this.Data[index] = value;
}
}
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
public void Dispose()
/// <summary>
/// Creates a new block
/// </summary>
/// <returns>The <see cref="BlockF"/></returns>
public static BlockF Create()
{
var block = default(BlockF);
block.Init();
return block;
}
/// <summary>
/// Returns an array of blocks of the given length.
/// </summary>
/// <param name="count">The number to create.</param>
/// <returns>The <see cref="T:BlockF[]"/></returns>
public static BlockF[] CreateArray(int count)
{
if (Data != null)
BlockF[] result = new BlockF[count];
for (int i = 0; i < result.Length; i++)
{
ArrayPool.Return(Data, true);
Data = null;
result[i].Init();
}
return result;
}
/// <summary>
/// Disposes of the collection of blocks
/// </summary>
/// <param name="blocks">The blocks.</param>
public static void DisposeAll(BlockF[] blocks)
{
for (int i = 0; i < blocks.Length; i++)
@ -195,22 +227,46 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Clears the block data
/// </summary>
public void Clear()
{
for (int i = 0; i < Data.Length; i++)
for (int i = 0; i < this.Data.Length; i++)
{
Data[i] = 0;
this.Data[i] = 0;
}
}
/// <summary>
/// Clones the current block
/// </summary>
/// <returns>The <see cref="Block"/></returns>
public BlockF Clone()
{
BlockF clone = Create();
Array.Copy(Data, clone.Data, BlockSize);
Array.Copy(this.Data, clone.Data, BlockSize);
return clone;
}
}
/// <summary>
/// Initializes the new block.
/// </summary>
public void Init()
{
// this.Data = new int[BlockSize];
this.Data = ArrayPool.Rent(BlockSize);
}
}
/// <inheritdoc />
public void Dispose()
{
// TODO: Refactor Block.Dispose() callers to always use 'using' or 'finally' statement!
if (this.Data != null)
{
ArrayPool.Return(this.Data, true);
this.Data = null;
}
}
}
}

35
src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.cs

@ -1,4 +1,5 @@

// <auto-generated />
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -8,6 +9,14 @@ namespace ImageSharp.Formats
{
internal partial struct Block8x8F
{
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
/// <summary>
/// Transpose the block into d
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TransposeInto(ref Block8x8F d)
{
@ -21,24 +30,12 @@ namespace ImageSharp.Formats
d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W;
}
public void CropInto(float min, float max, ref Block8x8F d)
{
Vector4 minVec = new Vector4(min);
Vector4 maxVec = new Vector4(max);
d.V0L = Vector4.Max(Vector4.Min(V0L, maxVec), minVec);d.V0R = Vector4.Max(Vector4.Min(V0R, maxVec), minVec);
d.V1L = Vector4.Max(Vector4.Min(V1L, maxVec), minVec);d.V1R = Vector4.Max(Vector4.Min(V1R, maxVec), minVec);
d.V2L = Vector4.Max(Vector4.Min(V2L, maxVec), minVec);d.V2R = Vector4.Max(Vector4.Min(V2R, maxVec), minVec);
d.V3L = Vector4.Max(Vector4.Min(V3L, maxVec), minVec);d.V3R = Vector4.Max(Vector4.Min(V3R, maxVec), minVec);
d.V4L = Vector4.Max(Vector4.Min(V4L, maxVec), minVec);d.V4R = Vector4.Max(Vector4.Min(V4R, maxVec), minVec);
d.V5L = Vector4.Max(Vector4.Min(V5L, maxVec), minVec);d.V5R = Vector4.Max(Vector4.Min(V5R, maxVec), minVec);
d.V6L = Vector4.Max(Vector4.Min(V6L, maxVec), minVec);d.V6R = Vector4.Max(Vector4.Min(V6R, maxVec), minVec);
d.V7L = Vector4.Max(Vector4.Min(V7L, maxVec), minVec);d.V7R = Vector4.Max(Vector4.Min(V7R, maxVec), minVec);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ColorifyInto(ref Block8x8F d)
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
{
d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4;
d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4;

45
src/ImageSharp/Formats/Jpg/Components/Block8x8F.Generated.tt

@ -4,19 +4,28 @@
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// <auto-generated />
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
<#
char[] coordz = new[] {'X', 'Y', 'Z', 'W'};
char[] coordz = {'X', 'Y', 'Z', 'W'};
#>
namespace ImageSharp.Formats
{
internal partial struct Block8x8F
{
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
/// <summary>
/// Transpose the block into d
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void TransposeInto(ref Block8x8F d)
{
@ -34,33 +43,7 @@ namespace ImageSharp.Formats
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; ";
//bld.Append(expression);
Write(expression);
}
//bld.AppendLine();
WriteLine("");
}
PopIndent();
//Write(bld.ToString());
#>
}
public void CropInto(float min, float max, ref Block8x8F d)
{
Vector4 minVec = new Vector4(min);
Vector4 maxVec = new Vector4(max);
<#
PushIndent(" ");
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 2; j++)
{
char side = j == 0 ? 'L' : 'R';
Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, maxVec), minVec);");
}
WriteLine("");
}
@ -68,8 +51,12 @@ namespace ImageSharp.Formats
#>
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ColorifyInto(ref Block8x8F d)
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// </summary>
/// <param name="d">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d)
{
<#

615
src/ImageSharp/Formats/Jpg/Components/Block8x8F.cs

@ -1,13 +1,15 @@
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// <copyright file="Block8x8F.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// </summary>
@ -37,64 +39,38 @@ namespace ImageSharp.Formats
public Vector4 V7L;
public Vector4 V7R;
public const int VectorCount = 16;
public const int ScalarCount = VectorCount*4;
private static readonly ArrayPool<float> ScalarArrayPool = ArrayPool<float>.Create(ScalarCount, 50);
public const int ScalarCount = VectorCount * 4;
/// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void LoadFrom(MutableSpan<float> source)
{
fixed (Vector4* ptr = &V0L)
{
Marshal.Copy(source.Data, source.Offset, (IntPtr) ptr, ScalarCount);
//float* fp = (float*)ptr;
//for (int i = 0; i < ScalarCount; i++)
//{
// fp[i] = source[i];
//}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(MutableSpan<float> dest)
{
fixed (Vector4* ptr = &V0L)
{
Marshal.Copy((IntPtr) ptr, dest.Data, dest.Offset, ScalarCount);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(float[] dest)
{
fixed (Vector4* ptr = &V0L)
fixed (void* ptr = &this.V0L)
{
Marshal.Copy((IntPtr) ptr, dest, 0, ScalarCount);
Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount);
}
}
/// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan<float> source)
{
Marshal.Copy(source.Data, source.Offset, (IntPtr) blockPtr, ScalarCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest)
{
Marshal.Copy((IntPtr) blockPtr, dest.Data, dest.Offset, ScalarCount);
Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount);
}
/// <summary>
/// Load raw 32bit floating point data from source
/// </summary>
internal unsafe void LoadFrom(MutableSpan<int> source)
{
fixed (Vector4* ptr = &V0L)
fixed (Vector4* ptr = &this.V0L)
{
float* fp = (float*) ptr;
float* fp = (float*)ptr;
for (int i = 0; i < ScalarCount; i++)
{
fp[i] = source[i];
@ -102,77 +78,50 @@ namespace ImageSharp.Formats
}
}
internal unsafe void CopyTo(MutableSpan<int> dest)
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(MutableSpan<float> dest)
{
fixed (Vector4* ptr = &V0L)
fixed (void* ptr = &this.V0L)
{
float* fp = (float*) ptr;
for (int i = 0; i < ScalarCount; i++)
{
dest[i] = (int) fp[i];
}
Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount);
}
}
public unsafe void TransposeInplace()
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(float[] dest)
{
fixed (Vector4* ptr = &V0L)
fixed (void* ptr = &this.V0L)
{
float* data = (float*) ptr;
for (int i = 1; i < 8; i++)
{
int i8 = i*8;
for (int j = 0; j < i; j++)
{
float tmp = data[i8 + j];
data[i8 + j] = data[j*8 + i];
data[j*8 + i] = tmp;
}
}
Marshal.Copy((IntPtr)ptr, dest, 0, ScalarCount);
}
}
/// <summary>
/// Reference implementation we can benchmark against
/// Copy raw 32bit floating point data to dest
/// </summary>
internal unsafe void TransposeInto_PinningImpl(ref Block8x8F destination)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest)
{
fixed (Vector4* sPtr = &V0L)
{
float* src = (float*) sPtr;
fixed (Vector4* dPtr = &destination.V0L)
{
float* dest = (float*) dPtr;
for (int i = 0; i < 8; i++)
{
int i8 = i*8;
for (int j = 0; j < 8; j++)
{
dest[j*8 + i] = src[i8 + j];
}
}
}
}
Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void TransposeInto(Block8x8F* sourcePtr, Block8x8F* destPtr)
/// <summary>
/// Copy raw 32bit floating point data to dest
/// </summary>
internal unsafe void CopyTo(MutableSpan<int> dest)
{
float* src = (float*) sourcePtr;
float* dest = (float*) destPtr;
for (int i = 0; i < 8; i++)
fixed (Vector4* ptr = &this.V0L)
{
int i8 = i*8;
for (int j = 0; j < 8; j++)
float* fp = (float*)ptr;
for (int i = 0; i < ScalarCount; i++)
{
dest[j*8 + i] = src[i8 + j];
dest[i] = (int)fp[i];
}
}
}
@ -180,151 +129,103 @@ namespace ImageSharp.Formats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void MultiplyAllInplace(Vector4 s)
{
V0L *= s;
V0R *= s;
V1L *= s;
V1R *= s;
V2L *= s;
V2R *= s;
V3L *= s;
V3R *= s;
V4L *= s;
V4R *= s;
V5L *= s;
V5R *= s;
V6L *= s;
V6R *= s;
V7L *= s;
V7R *= s;
this.V0L *= s;
this.V0R *= s;
this.V1L *= s;
this.V1R *= s;
this.V2L *= s;
this.V2R *= s;
this.V3L *= s;
this.V3R *= s;
this.V4L *= s;
this.V4R *= s;
this.V5L *= s;
this.V5R *= s;
this.V6L *= s;
this.V6R *= s;
this.V7L *= s;
this.V7R *= s;
}
// ReSharper disable once InconsistentNaming
public void IDCTInto(ref Block8x8F dest, ref Block8x8F temp)
/// <summary>
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
/// </summary>
/// <param name="dest">Destination</param>
/// <param name="temp">Temporary block provided by the caller</param>
public void TransformIDCTInto(ref Block8x8F dest, ref Block8x8F temp)
{
TransposeInto(ref temp);
temp.iDCT2D8x4_LeftPart(ref dest);
temp.iDCT2D8x4_RightPart(ref dest);
this.TransposeInto(ref temp);
temp.IDCT8x4_LeftPart(ref dest);
temp.IDCT8x4_RightPart(ref dest);
dest.TransposeInto(ref temp);
temp.iDCT2D8x4_LeftPart(ref dest);
temp.iDCT2D8x4_RightPart(ref dest);
dest.MultiplyAllInplace(_0_125);
}
temp.IDCT8x4_LeftPart(ref dest);
temp.IDCT8x4_RightPart(ref dest);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void IDCTInplace()
{
Block8x8F result = new Block8x8F();
Block8x8F temp = new Block8x8F();
IDCTInto(ref result, ref temp);
this = result;
dest.MultiplyAllInplace(c_0_125);
}
private static readonly Vector4 _1_175876 = new Vector4(1.175876f);
private static readonly Vector4 _1_961571 = new Vector4(-1.961571f);
private static readonly Vector4 _0_390181 = new Vector4(-0.390181f);
private static readonly Vector4 _0_899976 = new Vector4(-0.899976f);
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 _0_298631 = new Vector4(0.298631f);
private static readonly Vector4 _2_053120 = new Vector4(2.053120f);
private static readonly Vector4 _3_072711 = new Vector4(3.072711f);
private static readonly Vector4 _1_501321 = new Vector4(1.501321f);
private static readonly Vector4 _0_541196 = new Vector4(0.541196f);
private static readonly Vector4 _1_847759 = new Vector4(-1.847759f);
private static readonly Vector4 _0_765367 = new Vector4(0.765367f);
private static readonly Vector4 _0_125 = new Vector4(0.1250f);
private static readonly Vector4 c_1_175876 = new Vector4(1.175876f);
private static readonly Vector4 c_1_961571 = new Vector4(-1.961571f);
private static readonly Vector4 c_0_390181 = new Vector4(-0.390181f);
private static readonly Vector4 c_0_899976 = new Vector4(-0.899976f);
private static readonly Vector4 c_2_562915 = new Vector4(-2.562915f);
private static readonly Vector4 c_0_298631 = new Vector4(0.298631f);
private static readonly Vector4 c_2_053120 = new Vector4(2.053120f);
private static readonly Vector4 c_3_072711 = new Vector4(3.072711f);
private static readonly Vector4 c_1_501321 = new Vector4(1.501321f);
private static readonly Vector4 c_0_541196 = new Vector4(0.541196f);
private static readonly Vector4 c_1_847759 = new Vector4(-1.847759f);
private static readonly Vector4 c_0_765367 = new Vector4(0.765367f);
private static readonly Vector4 c_0_125 = new Vector4(0.1250f);
/// <summary>
/// Do IDCT internal operations on the left part of the block. Original source:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary>
/// <param name="d">Destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void iDCT2D8x4_LeftPart(ref Block8x8F d)
internal void IDCT8x4_LeftPart(ref Block8x8F d)
{
/*
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i;
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); }
*/
/*
0: 1.414214
1: 1.387040
2: 1.306563
3:
4: 1.000000
5: 0.785695
6:
7: 0.275899
*/
Vector4 my1 = V1L;
Vector4 my7 = V7L;
Vector4 my1 = this.V1L;
Vector4 my7 = this.V7L;
Vector4 mz0 = my1 + my7;
Vector4 my3 = V3L;
Vector4 my3 = this.V3L;
Vector4 mz2 = my3 + my7;
Vector4 my5 = V5L;
Vector4 my5 = this.V5L;
Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5;
Vector4 mz4 = ((mz0 + mz1)*_1_175876);
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
//z4 = (z0 + z1) * r[3];
mz2 = mz2*_1_961571 + mz4;
mz3 = mz3*_0_390181 + mz4;
mz0 = mz0*_0_899976;
mz1 = mz1*_2_562915;
/*
-0.899976
-2.562915
-1.961571
-0.390181
z0 = z0 * (-r[3] + r[7]);
z1 = z1 * (-r[3] - r[1]);
z2 = z2 * (-r[3] - r[5]) + z4;
z3 = z3 * (-r[3] + r[5]) + z4;*/
Vector4 mb3 = my7*_0_298631 + mz0 + mz2;
Vector4 mb2 = my5*_2_053120 + mz1 + mz3;
Vector4 mb1 = my3*_3_072711 + mz1 + mz2;
Vector4 mb0 = my1*_1_501321 + mz0 + mz3;
/*
0.298631
2.053120
3.072711
1.501321
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2;
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3;
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2;
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3;
*/
Vector4 my2 = V2L;
Vector4 my6 = V6L;
mz4 = (my2 + my6)*_0_541196;
Vector4 my0 = V0L;
Vector4 my4 = V4L;
Vector4 mz4 = ((mz0 + mz1) * c_1_175876);
mz2 = (mz2 * c_1_961571) + mz4;
mz3 = (mz3 * c_0_390181) + mz4;
mz0 = mz0 * c_0_899976;
mz1 = mz1 * c_2_562915;
Vector4 mb3 = (my7 * c_0_298631) + mz0 + mz2;
Vector4 mb2 = (my5 * c_2_053120) + mz1 + mz3;
Vector4 mb1 = (my3 * c_3_072711) + mz1 + mz2;
Vector4 mb0 = (my1 * c_1_501321) + mz0 + mz3;
Vector4 my2 = this.V2L;
Vector4 my6 = this.V6L;
mz4 = (my2 + my6) * c_0_541196;
Vector4 my0 = this.V0L;
Vector4 my4 = this.V4L;
mz0 = my0 + my4;
mz1 = my0 - my4;
mz2 = mz4 + my6*_1_847759;
mz3 = mz4 + my2*_0_765367;
mz2 = mz4 + (my6 * c_1_847759);
mz3 = mz4 + (my2 * c_0_765367);
my0 = mz0 + mz3;
my3 = mz0 - mz3;
my1 = mz1 + mz2;
my2 = mz1 - mz2;
/*
1.847759
0.765367
z4 = (y[2] + y[6]) * r[6];
z0 = y[0] + y[4]; z1 = y[0] - y[4];
z2 = z4 - y[6] * (r[2] + r[6]);
z3 = z4 + y[2] * (r[2] - r[6]);
a0 = z0 + z3; a3 = z0 - z3;
a1 = z1 + z2; a2 = z1 - z2;
*/
d.V0L = my0 + mb0;
d.V7L = my0 - mb0;
@ -334,104 +235,53 @@ namespace ImageSharp.Formats
d.V5L = my2 - mb2;
d.V3L = my3 + mb3;
d.V4L = my3 - mb3;
/*
x[0] = a0 + b0; x[7] = a0 - b0;
x[1] = a1 + b1; x[6] = a1 - b1;
x[2] = a2 + b2; x[5] = a2 - b2;
x[3] = a3 + b3; x[4] = a3 - b3;
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; }
*/
}
/// <summary>
/// Do IDCT internal operations on the right part of the block.
/// Original source:
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void iDCT2D8x4_RightPart(ref Block8x8F d)
internal void IDCT8x4_RightPart(ref Block8x8F d)
{
/*
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i;
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); }
*/
/*
0: 1.414214
1: 1.387040
2: 1.306563
3:
4: 1.000000
5: 0.785695
6:
7: 0.275899
*/
Vector4 my1 = V1R;
Vector4 my7 = V7R;
Vector4 my1 = this.V1R;
Vector4 my7 = this.V7R;
Vector4 mz0 = my1 + my7;
Vector4 my3 = V3R;
Vector4 my3 = this.V3R;
Vector4 mz2 = my3 + my7;
Vector4 my5 = V5R;
Vector4 my5 = this.V5R;
Vector4 mz1 = my3 + my5;
Vector4 mz3 = my1 + my5;
Vector4 mz4 = ((mz0 + mz1)*_1_175876);
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
//z4 = (z0 + z1) * r[3];
mz2 = mz2*_1_961571 + mz4;
mz3 = mz3*_0_390181 + mz4;
mz0 = mz0*_0_899976;
mz1 = mz1*_2_562915;
/*
-0.899976
-2.562915
-1.961571
-0.390181
z0 = z0 * (-r[3] + r[7]);
z1 = z1 * (-r[3] - r[1]);
z2 = z2 * (-r[3] - r[5]) + z4;
z3 = z3 * (-r[3] + r[5]) + z4;*/
Vector4 mb3 = my7*_0_298631 + mz0 + mz2;
Vector4 mb2 = my5*_2_053120 + mz1 + mz3;
Vector4 mb1 = my3*_3_072711 + mz1 + mz2;
Vector4 mb0 = my1*_1_501321 + mz0 + mz3;
/*
0.298631
2.053120
3.072711
1.501321
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2;
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3;
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2;
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3;
*/
Vector4 my2 = V2R;
Vector4 my6 = V6R;
mz4 = (my2 + my6)*_0_541196;
Vector4 my0 = V0R;
Vector4 my4 = V4R;
Vector4 mz4 = (mz0 + mz1) * c_1_175876;
mz2 = (mz2 * c_1_961571) + mz4;
mz3 = (mz3 * c_0_390181) + mz4;
mz0 = mz0 * c_0_899976;
mz1 = mz1 * c_2_562915;
Vector4 mb3 = (my7 * c_0_298631) + mz0 + mz2;
Vector4 mb2 = (my5 * c_2_053120) + mz1 + mz3;
Vector4 mb1 = (my3 * c_3_072711) + mz1 + mz2;
Vector4 mb0 = (my1 * c_1_501321) + mz0 + mz3;
Vector4 my2 = this.V2R;
Vector4 my6 = this.V6R;
mz4 = (my2 + my6) * c_0_541196;
Vector4 my0 = this.V0R;
Vector4 my4 = this.V4R;
mz0 = my0 + my4;
mz1 = my0 - my4;
mz2 = mz4 + my6*_1_847759;
mz3 = mz4 + my2*_0_765367;
mz2 = mz4 + (my6 * c_1_847759);
mz3 = mz4 + (my2 * c_0_765367);
my0 = mz0 + mz3;
my3 = mz0 - mz3;
my1 = mz1 + mz2;
my2 = mz1 - mz2;
/*
1.847759
0.765367
z4 = (y[2] + y[6]) * r[6];
z0 = y[0] + y[4]; z1 = y[0] - y[4];
z2 = z4 - y[6] * (r[2] + r[6]);
z3 = z4 + y[2] * (r[2] - r[6]);
a0 = z0 + z3; a3 = z0 - z3;
a1 = z1 + z2; a2 = z1 - z2;
*/
d.V0R = my0 + mb0;
d.V7R = my0 - mb0;
@ -441,37 +291,6 @@ namespace ImageSharp.Formats
d.V5R = my2 - mb2;
d.V3R = my3 + mb3;
d.V4R = my3 - mb3;
/*
x[0] = a0 + b0; x[7] = a0 - b0;
x[1] = a1 + b1; x[6] = a1 - b1;
x[2] = a2 + b2; x[5] = a2 - b2;
x[3] = a3 + b3; x[4] = a3 - b3;
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; }
*/
}
internal static void SuchIDCT(ref Block block)
{
Block8x8F source = new Block8x8F();
source.LoadFrom(block.Data);
Block8x8F dest = new Block8x8F();
Block8x8F temp = new Block8x8F();
source.IDCTInto(ref dest, ref temp);
dest.CopyTo(block.Data);
}
internal static void SuchIDCT(ref BlockF block)
{
Block8x8F source = new Block8x8F();
source.LoadFrom(block.Data);
Block8x8F dest = new Block8x8F();
Block8x8F temp = new Block8x8F();
source.IDCTInto(ref dest, ref temp);
dest.CopyTo(block.Data);
}
public unsafe float this[int idx]
@ -481,138 +300,106 @@ namespace ImageSharp.Formats
{
fixed (Block8x8F* p = &this)
{
float* fp = (float*) p;
float* fp = (float*)p;
return fp[idx];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
fixed (Block8x8F* p = &this)
{
float* fp = (float*) p;
float* fp = (float*)p;
fp[idx] = value;
}
}
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx)
{
float* fp = (float*) blockPtr;
float* fp = (float*)blockPtr;
return fp[idx];
}
/// <summary>
/// Pointer-based "Indexer" (setter part)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value)
{
float* fp = (float*) blockPtr;
float* fp = (float*)blockPtr;
fp[idx] = value;
}
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Clear()
{
this = new Block8x8F(); // LOL C# Plz!
// The cheapest way to do this in C#:
this = new Block8x8F();
}
/// <summary>
/// TODO: Should be removed when BlockF goes away
/// </summary>
/// <param name="legacyBlock"></param>
internal void LoadFrom(ref BlockF legacyBlock)
{
LoadFrom(legacyBlock.Data);
this.LoadFrom(legacyBlock.Data);
}
/// <summary>
/// TODO: Should be removed when BlockF goes away
/// </summary>
/// <param name="legacyBlock"></param>
internal void CopyTo(ref BlockF legacyBlock)
{
CopyTo(legacyBlock.Data);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ToColorByte(float c)
{
if (c < -128)
{
return 0;
}
else if (c > 127)
{
return 255;
}
else
{
c += 128;
return (byte) c;
}
this.CopyTo(legacyBlock.Data);
}
internal unsafe void CopyColorsTo(MutableSpan<byte> buffer, int stride)
{
fixed (Block8x8F* p = &this)
{
float* b = (float*) p;
for (int y = 0; y < 8; y++)
{
int y8 = y*8;
int yStride = y*stride;
for (int x = 0; x < 8; x++)
{
float c = b[y8 + x];
if (c < -128)
{
c = 0;
}
else if (c > 127)
{
c = 255;
}
else
{
c += 128;
}
buffer[yStride + x] = (byte) c;
}
}
}
}
private static readonly Vector4 CMin4 = new Vector4(-128f);
private static readonly Vector4 CMax4 = new Vector4(127f);
private static readonly Vector4 COff4 = new Vector4(128f);
/// <summary>
/// Level shift by +128, clip to [0, 255], and write to buffer.
/// Level shift by +128, clip to [0, 255], and write to buffer.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe void CopyColorsTo(
MutableSpan<byte> buffer,
int stride,
Block8x8F* temp)
internal unsafe void CopyColorsTo(MutableSpan<byte> buffer, int stride, Block8x8F* temp)
{
ColorifyInto(ref *temp);
this.TransformByteConvetibleColorValuesInto(ref *temp);
float* src = (float*) temp;
float* src = (float*)temp;
for (int i = 0; i < 8; i++)
{
buffer[0] = (byte) src[0];
buffer[1] = (byte) src[1];
buffer[2] = (byte) src[2];
buffer[3] = (byte) src[3];
buffer[4] = (byte) src[4];
buffer[5] = (byte) src[5];
buffer[6] = (byte) src[6];
buffer[7] = (byte) src[7];
buffer[0] = (byte)src[0];
buffer[1] = (byte)src[1];
buffer[2] = (byte)src[2];
buffer[3] = (byte)src[3];
buffer[4] = (byte)src[4];
buffer[5] = (byte)src[5];
buffer[6] = (byte)src[6];
buffer[7] = (byte)src[7];
buffer.AddOffset(stride);
src += 8;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void UnZig(Block8x8F* block, Block8x8F* qt, int* unzigPtr)
{
float* b = (float*)block;
float* qtp = (float*)qt;
for (int zig = 0; zig < BlockF.BlockSize; zig++)
{
float* unzigPos = b + unzigPtr[zig];
float val = *unzigPos;
val *= qtp[zig];
*unzigPos = val;
}
}
}
}

141
src/ImageSharp/Formats/Jpg/Components/Bytes.cs

@ -2,25 +2,28 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
/// <summary>
/// Bytes is a byte buffer, similar to a stream, except that it
/// has to be able to unread more than 1 byte, due to byte stuffing.
/// Byte stuffing is specified in section F.1.2.3.
/// </summary>
internal class Bytes
internal struct Bytes : IDisposable
{
private static readonly ArrayPool<byte> ArrayPool = ArrayPool<byte>.Create(4096, 50);
/// <summary>
/// Initializes a new instance of the <see cref="Bytes"/> class.
/// Creates a new instance of the <see cref="Bytes"/>, and initializes it's buffer.
/// </summary>
public Bytes()
public static Bytes Create()
{
this.Buffer = new byte[4096];
this.I = 0;
this.J = 0;
this.UnreadableBytes = 0;
return new Bytes { Buffer = ArrayPool.Rent(4096) };
}
/// <summary>
@ -28,16 +31,128 @@ namespace ImageSharp.Formats
/// buffer[i:j] are the buffered bytes read from the underlying
/// stream that haven't yet been passed further on.
/// </summary>
public byte[] Buffer { get; set; }
public byte[] Buffer;
public int I { get; set; }
public int I;
public int J { get; set; }
public int J;
/// <summary>
/// Gets or sets the unreadable bytes. The number of bytes to back up i after
/// overshooting. It can be 0, 1 or 2.
/// </summary>
public int UnreadableBytes { get; set; }
public int UnreadableBytes;
public void Dispose()
{
if (this.Buffer != null) ArrayPool.Return(this.Buffer);
this.Buffer = null;
}
/// <summary>
/// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
internal byte ReadByteStuffedByte(Stream inputStream, out JpegDecoderCore.ErrorCodes errorCode)
{
byte x;
errorCode = JpegDecoderCore.ErrorCodes.NoError;
// Take the fast path if bytes.buf contains at least two bytes.
if (this.I + 2 <= this.J)
{
x = this.Buffer[this.I];
this.I++;
this.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF)
{
return x;
}
if (this.Buffer[this.I] != 0x00)
{
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0;
// throw new MissingFF00Exception();
}
this.I++;
this.UnreadableBytes = 2;
return JpegConstants.Markers.XFF;
}
this.UnreadableBytes = 0;
x = this.ReadByte(inputStream);
this.UnreadableBytes = 1;
if (x != JpegConstants.Markers.XFF)
{
return x;
}
x = this.ReadByte(inputStream);
this.UnreadableBytes = 2;
if (x != 0x00)
{
errorCode = JpegDecoderCore.ErrorCodes.MissingFF00;
return 0;
// throw new MissingFF00Exception();
}
return JpegConstants.Markers.XFF;
}
/// <summary>
/// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing.
/// </summary>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadByte(Stream inputStream)
{
while (this.I == this.J)
{
this.Fill(inputStream);
}
byte x = this.Buffer[this.I];
this.I++;
this.UnreadableBytes = 0;
return x;
}
/// <summary>
/// Fills up the bytes buffer from the underlying stream.
/// It should only be called when there are no unread bytes in bytes.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Fill(Stream inputStream)
{
if (this.I != this.J)
{
throw new ImageFormatException("Fill called when unread bytes exist.");
}
// Move the last 2 bytes to the start of the buffer, in case we need
// to call UnreadByteStuffedByte.
if (this.J > 2)
{
this.Buffer[0] = this.Buffer[this.J - 2];
this.Buffer[1] = this.Buffer[this.J - 1];
this.I = 2;
this.J = 2;
}
// Fill in the rest of the buffer.
int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J);
if (n == 0)
{
throw new JpegDecoderCore.EOFException();
}
this.J += n;
}
}
}
}

41
src/ImageSharp/Formats/Jpg/Components/Huffman.cs

@ -2,42 +2,29 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System;
using System.Buffers;
namespace ImageSharp.Formats
{
using System;
using System.Buffers;
/// <summary>
/// Represents a Huffman tree
/// </summary>
internal struct Huffman : IDisposable
{
private static ArrayPool<ushort> UshortBuffer =
//ArrayPool<ushort>.Shared;
ArrayPool<ushort>.Create(1 << JpegDecoderCore.LutSize, 50);
private static ArrayPool<ushort> UshortBuffer = ArrayPool<ushort>.Create(1 << JpegDecoderCore.LutSize, 50);
private static ArrayPool<byte> ByteBuffer =
//ArrayPool<byte>.Shared;
ArrayPool<byte>.Create(JpegDecoderCore.MaxNCodes, 50);
private static ArrayPool<byte> ByteBuffer = ArrayPool<byte>.Create(JpegDecoderCore.MaxNCodes, 50);
private static readonly ArrayPool<int> IntBuffer =
//ArrayPool<int>.Shared;
ArrayPool<int>.Create(JpegDecoderCore.MaxCodeLength, 50);
private static readonly ArrayPool<int> IntBuffer = ArrayPool<int>.Create(JpegDecoderCore.MaxCodeLength, 50);
public void Init(int lutSize, int maxNCodes, int maxCodeLength)
{
//this.Lut = new ushort[1 << lutSize];
//this.Values = new byte[maxNCodes];
//this.MinCodes = new int[maxCodeLength];
//this.MaxCodes = new int[maxCodeLength];
//this.Indices = new int[maxCodeLength];
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); ;
this.MaxCodes = IntBuffer.Rent(maxCodeLength);
this.Indices = IntBuffer.Rent(maxCodeLength);
}
/// <summary>
@ -77,13 +64,11 @@ namespace ImageSharp.Formats
public void Dispose()
{
UshortBuffer.Return(Lut, true);
ByteBuffer.Return(Values, true);
IntBuffer.Return(MinCodes, true);
IntBuffer.Return(MaxCodes, true);
IntBuffer.Return(Indices, true);
UshortBuffer.Return(this.Lut, true);
ByteBuffer.Return(this.Values, true);
IntBuffer.Return(this.MinCodes, true);
IntBuffer.Return(this.MaxCodes, true);
IntBuffer.Return(this.Indices, true);
}
}
}

2
src/ImageSharp/Formats/Jpg/Components/IDCT.cs

@ -3,8 +3,6 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Numerics;
namespace ImageSharp.Formats
{
/// <summary>

46
src/ImageSharp/Formats/Jpg/Components/MutableSpan.cs

@ -1,52 +1,68 @@
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
// <copyright file="MutableSpan.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.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Like corefxlab Span, but with an AddOffset() method for efficiency.
/// TODO: When Span will be official, consider replacing this class!
/// </summary>
/// <see cref="https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs"/>
/// <see>
/// <cref>https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs</cref>
/// </see>
/// <typeparam name="T"></typeparam>
internal struct MutableSpan<T>
{
public T[] Data;
public int Offset;
public int TotalCount => Data.Length - Offset;
public int TotalCount => this.Data.Length - this.Offset;
public MutableSpan(int size, int offset = 0)
{
Data = new T[size];
Offset = offset;
this.Data = new T[size];
this.Offset = offset;
}
public MutableSpan(T[] data, int offset = 0)
{
Data = data;
Offset = offset;
this.Data = data;
this.Offset = offset;
}
public T this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Data[idx + Offset]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)] set { Data[idx + Offset] = value; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Data[idx + this.Offset];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.Data[idx + this.Offset] = value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MutableSpan<T> Slice(int offset)
{
return new MutableSpan<T>(Data, Offset + offset);
return new MutableSpan<T>(this.Data, this.Offset + offset);
}
public static implicit operator MutableSpan<T>(T[] data) => new MutableSpan<T>(data, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddOffset(int offset)
{
Offset += offset;
this.Offset += offset;
}
}
@ -89,7 +105,5 @@ namespace ImageSharp.Formats
data[2] = (int)v.Z;
data[3] = (int)v.W;
}
}
}

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

File diff suppressed because it is too large

11
src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

@ -541,12 +541,12 @@ namespace ImageSharp.Formats
/// <param name="cbBlock">The red chroma block.</param>
/// <param name="crBlock">The blue chroma block.</param>
// ReSharper disable StyleCop.SA1305
private void ToYCbCr<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int x, int y,
ref Block yBlock, ref Block cbBlock, ref Block crBlock)
// ReSharper restore StyleCop.SA1305
private void ToYCbCr<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int x, int y, ref Block yBlock, ref Block cbBlock, ref Block crBlock)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
// ReSharper restore StyleCop.SA1305
int xmax = pixels.Width - 1;
int ymax = pixels.Height - 1;
byte[] color = new byte[3];
@ -852,6 +852,7 @@ namespace ImageSharp.Formats
Block b = Block.Create();
Block cb = Block.Create();
Block cr = Block.Create();
// ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
@ -865,6 +866,7 @@ namespace ImageSharp.Formats
prevDCCr = this.WriteBlock(ref cr, QuantIndex.Chrominance, prevDCCr);
}
}
b.Dispose();
cb.Dispose();
cr.Dispose();
@ -884,9 +886,10 @@ namespace ImageSharp.Formats
Block b = Block.Create();
Block[] cb = Block.CreateArray(4);
Block[] cr = Block.CreateArray(4);
// ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
for (int y = 0; y < pixels.Height; y += 16)
{
for (int x = 0; x < pixels.Width; x += 16)

29
src/ImageSharp/Formats/Png/Filters/AverageFilter.cs

@ -5,8 +5,6 @@
namespace ImageSharp.Formats
{
using System;
/// <summary>
/// The Average filter uses the average of the two neighboring pixels (left and above) to predict
/// the value of a pixel.
@ -19,29 +17,22 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <returns>
/// The <see cref="T:byte[]"/>
/// </returns>
public static byte[] Decode(byte[] scanline, byte[] previousScanline, int bytesPerPixel)
public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
byte[] result = new byte[scanline.Length];
fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline)
fixed (byte* res = result)
{
for (int x = 1; x < scanline.Length; x++)
for (int x = 1; x < bytesPerScanline; x++)
{
byte left = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel];
byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel];
byte above = prev[x];
res[x] = (byte)((scan[x] + Average(left, above)) % 256);
scan[x] = (byte)((scan[x] + Average(left, above)) % 256);
}
}
return result;
}
/// <summary>
@ -49,11 +40,9 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel)
{
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
fixed (byte* scan = scanline)
@ -62,7 +51,7 @@ namespace ImageSharp.Formats
{
res[0] = 3;
for (int x = 0; x < bytesPerScanline; x++)
for (int x = 0; x < scanline.Length; x++)
{
byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
byte above = prev[x];
@ -70,8 +59,6 @@ namespace ImageSharp.Formats
res[x + 1] = (byte)((scan[x] - Average(left, above)) % 256);
}
}
return result;
}
/// <summary>

7
src/ImageSharp/Formats/Png/Filters/NoneFilter.cs

@ -29,13 +29,12 @@ namespace ImageSharp.Formats
/// Encodes the scanline
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
public static void Encode(byte[] scanline, byte[] result, int bytesPerScanline)
/// <param name="result">The filtered scanline result.</param>
public static void Encode(byte[] scanline, byte[] result)
{
// Insert a byte before the data.
result[0] = 0;
Buffer.BlockCopy(scanline, 0, result, 1, bytesPerScanline);
Buffer.BlockCopy(scanline, 0, result, 1, scanline.Length);
}
}
}

25
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -20,28 +20,23 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Decode(byte[] scanline, byte[] previousScanline, int bytesPerPixel)
public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
byte[] result = new byte[scanline.Length];
fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline)
fixed (byte* res = result)
{
for (int x = 1; x < scanline.Length; x++)
for (int x = 1; x < bytesPerScanline; x++)
{
byte left = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel];
byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel];
byte above = prev[x];
byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : prev[x - bytesPerPixel];
res[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256);
scan[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256);
}
}
return result;
}
/// <summary>
@ -49,11 +44,9 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel)
{
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
fixed (byte* scan = scanline)
@ -62,7 +55,7 @@ namespace ImageSharp.Formats
{
res[0] = 4;
for (int x = 0; x < bytesPerScanline; x++)
for (int x = 0; x < scanline.Length; x++)
{
byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
byte above = prev[x];
@ -71,8 +64,6 @@ namespace ImageSharp.Formats
res[x + 1] = (byte)((scan[x] - PaethPredicator(left, above, upperLeft)) % 256);
}
}
return result;
}
/// <summary>

26
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -16,36 +16,28 @@ namespace ImageSharp.Formats
/// Decodes the scanline
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Decode(byte[] scanline, int bytesPerPixel)
public static void Decode(byte[] scanline, int bytesPerScanline, int bytesPerPixel)
{
// Sub(x) + Raw(x-bpp)
byte[] result = new byte[scanline.Length];
fixed (byte* scan = scanline)
fixed (byte* res = result)
{
for (int x = 1; x < scanline.Length; x++)
for (int x = 1; x < bytesPerScanline; x++)
{
byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel];
res[x] = (byte)((scan[x] + priorRawByte) % 256);
byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel];
scan[x] = (byte)((scan[x] + priorRawByte) % 256);
}
}
return result;
}
/// <summary>
/// Encodes the scanline
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="result">The filtered scanline result.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
public static void Encode(byte[] scanline, byte[] result, int bytesPerPixel)
{
// Sub(x) = Raw(x) - Raw(x-bpp)
fixed (byte* scan = scanline)
@ -53,15 +45,13 @@ namespace ImageSharp.Formats
{
res[0] = 1;
for (int x = 0; x < bytesPerScanline; x++)
for (int x = 0; x < scanline.Length; x++)
{
byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
res[x + 1] = (byte)((scan[x] - priorRawByte) % 256);
}
}
return result;
}
}
}

23
src/ImageSharp/Formats/Png/Filters/UpFilter.cs

@ -17,25 +17,20 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="scanline">The scanline to decode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Decode(byte[] scanline, byte[] previousScanline)
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline)
{
// Up(x) + Prior(x)
byte[] result = new byte[scanline.Length];
fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline)
fixed (byte* res = result)
{
for (int x = 1; x < scanline.Length; x++)
for (int x = 1; x < bytesPerScanline; x++)
{
byte above = prev[x];
res[x] = (byte)((scan[x] + above) % 256);
scan[x] = (byte)((scan[x] + above) % 256);
}
}
return result;
}
/// <summary>
@ -43,10 +38,8 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
/// <param name="result">The filtered scanline result.</param>
public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result)
{
// Up(x) = Raw(x) - Prior(x)
fixed (byte* scan = scanline)
@ -55,15 +48,13 @@ namespace ImageSharp.Formats
{
res[0] = 2;
for (int x = 0; x < bytesPerScanline; x++)
for (int x = 0; x < scanline.Length; x++)
{
byte above = prev[x];
res[x + 1] = (byte)((scan[x] - above) % 256);
}
}
return result;
}
}
}

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

@ -6,11 +6,14 @@
namespace ImageSharp.Formats
{
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using static ComparableExtensions;
/// <summary>
/// Performs the png decoding operation.
/// </summary>
@ -21,6 +24,26 @@ namespace ImageSharp.Formats
/// </summary>
private static readonly Dictionary<int, byte[]> ColorTypes = new Dictionary<int, byte[]>();
/// <summary>
/// The amount to increment when processing each column per scanline for each interlaced pass
/// </summary>
private static readonly int[] Adam7ColumnIncrement = { 8, 8, 4, 4, 2, 2, 1 };
/// <summary>
/// The index to start at when processing each column per scanline for each interlaced pass
/// </summary>
private static readonly int[] Adam7FirstColumn = { 0, 4, 0, 2, 0, 1, 0 };
/// <summary>
/// The index to start at when processing each row per scanline for each interlaced pass
/// </summary>
private static readonly int[] Adam7FirstRow = { 0, 0, 4, 0, 2, 0, 1 };
/// <summary>
/// The amount to increment when processing each row per scanline for each interlaced pass
/// </summary>
private static readonly int[] Adam7RowIncrement = { 8, 8, 8, 4, 4, 2, 2 };
/// <summary>
/// Reusable buffer for reading chunk types.
/// </summary>
@ -41,6 +64,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly char[] chars = new char[4];
/// <summary>
/// Reusable crc for validating chunks.
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The stream to decode from.
/// </summary>
@ -130,39 +158,49 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Image does not end with end chunk.");
}
switch (currentChunk.Type)
try
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(currentImage, currentChunk.Data);
break;
case PngChunkTypes.Data:
dataStream.Write(currentChunk.Data, 0, currentChunk.Data.Length);
break;
case PngChunkTypes.Palette:
this.palette = currentChunk.Data;
image.Quality = this.palette.Length / 3;
break;
case PngChunkTypes.PaletteAlpha:
this.paletteAlpha = currentChunk.Data;
break;
case PngChunkTypes.Text:
this.ReadTextChunk(currentImage, currentChunk.Data);
break;
case PngChunkTypes.End:
isEndChunkReached = true;
break;
switch (currentChunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(currentImage, currentChunk.Data);
break;
case PngChunkTypes.Data:
dataStream.Write(currentChunk.Data, 0, currentChunk.Length);
break;
case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length);
this.palette = pal;
image.Quality = pal.Length / 3;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length);
this.paletteAlpha = alpha;
break;
case PngChunkTypes.Text:
this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length);
break;
case PngChunkTypes.End:
isEndChunkReached = true;
break;
}
}
finally
{
// Data is rented in ReadChunkData()
ArrayPool<byte>.Shared.Return(currentChunk.Data);
}
}
if (this.header.Width > image.MaxWidth || this.header.Height > image.MaxHeight)
{
throw new ArgumentOutOfRangeException(
$"The input png '{this.header.Width}x{this.header.Height}' is bigger than the "
+ $"max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{image.MaxWidth}x{image.MaxHeight}'");
}
image.InitPixels(this.header.Width, this.header.Height);
@ -174,6 +212,51 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits.
/// </summary>
/// <param name="source">The bytes to convert from. Cannot be null.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bits">The number of bits per value.</param>
/// <returns>The resulting <see cref="T:byte[]"/> array. Is never null.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
private static byte[] ToArrayByBitsLength(byte[] source, int bytesPerScanline, int bits)
{
Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThan(bits, 0, nameof(bits));
byte[] result;
if (bits < 8)
{
result = new byte[bytesPerScanline * 8 / bits];
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
// ReSharper disable once ForCanBeConvertedToForeach
// First byte is the marker so skip.
for (int i = 1; i < bytesPerScanline; i++)
{
byte b = source[i];
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
result[resultOffset] = (byte)colorIndex;
resultOffset++;
}
}
}
else
{
result = source;
}
return result;
}
/// <summary>
/// Reads the data chunk containing physical dimension data.
/// </summary>
@ -222,10 +305,13 @@ namespace ImageSharp.Formats
/// <summary>
/// Calculates the scanline length.
/// </summary>
/// <returns>The <see cref="int"/> representing the length.</returns>
private int CalculateScanlineLength()
/// <param name="width">The width of the row.</param>
/// <returns>
/// The <see cref="int"/> representing the length.
/// </returns>
private int CalculateScanlineLength(int width)
{
int scanlineLength = this.header.Width * this.header.BitDepth * this.bytesPerPixel;
int scanlineLength = width * this.header.BitDepth * this.bytesPerPixel;
int amount = scanlineLength % 8;
if (amount != 0)
@ -248,7 +334,7 @@ namespace ImageSharp.Formats
where TPacked : struct
{
this.bytesPerPixel = this.CalculateBytesPerPixel();
this.bytesPerScanline = this.CalculateScanlineLength() + 1;
this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1;
this.bytesPerSample = 1;
if (this.header.BitDepth >= 8)
{
@ -258,7 +344,14 @@ namespace ImageSharp.Formats
dataStream.Position = 0;
using (ZlibInflateStream compressedStream = new ZlibInflateStream(dataStream))
{
this.DecodePixelData(compressedStream, pixels);
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{
this.DecodeInterlacedPixelData(compressedStream, pixels);
}
else
{
this.DecodePixelData(compressedStream, pixels);
}
}
}
@ -273,57 +366,155 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
// TODO: ArrayPool<byte>.Shared.Rent(this.bytesPerScanline)
byte[] previousScanline = new byte[this.bytesPerScanline];
byte[] scanline = new byte[this.bytesPerScanline];
for (int y = 0; y < this.header.Height; y++)
{
compressedStream.Read(scanline, 0, this.bytesPerScanline);
byte[] previousScanline = ArrayPool<byte>.Shared.Rent(this.bytesPerScanline);
byte[] scanline = ArrayPool<byte>.Shared.Rent(this.bytesPerScanline);
FilterType filterType = (FilterType)scanline[0];
byte[] defilteredScanline;
// Zero out the previousScanline, because the bytes that are rented from the arraypool may not be zero.
Array.Clear(previousScanline, 0, this.bytesPerScanline);
// TODO: It would be good if we can reduce the memory usage here - Each filter is creating a new row.
// Every time I try to use the same approach as I have in the encoder though I keep messing up.
// Fingers crossed someone with a big brain and a kind heart will come along and finish optimizing this for me.
switch (filterType)
try
{
for (int y = 0; y < this.header.Height; y++)
{
case FilterType.None:
compressedStream.Read(scanline, 0, this.bytesPerScanline);
defilteredScanline = NoneFilter.Decode(scanline);
FilterType filterType = (FilterType)scanline[0];
break;
switch (filterType)
{
case FilterType.None:
case FilterType.Sub:
NoneFilter.Decode(scanline);
defilteredScanline = SubFilter.Decode(scanline, this.bytesPerPixel);
break;
break;
case FilterType.Sub:
case FilterType.Up:
SubFilter.Decode(scanline, this.bytesPerScanline, this.bytesPerPixel);
defilteredScanline = UpFilter.Decode(scanline, previousScanline);
break;
break;
case FilterType.Up:
case FilterType.Average:
UpFilter.Decode(scanline, previousScanline, this.bytesPerScanline);
defilteredScanline = AverageFilter.Decode(scanline, previousScanline, this.bytesPerPixel);
break;
case FilterType.Average:
break;
AverageFilter.Decode(scanline, previousScanline, this.bytesPerScanline, this.bytesPerPixel);
break;
case FilterType.Paeth:
case FilterType.Paeth:
PaethFilter.Decode(scanline, previousScanline, this.bytesPerScanline, this.bytesPerPixel);
break;
defilteredScanline = PaethFilter.Decode(scanline, previousScanline, this.bytesPerPixel);
default:
throw new ImageFormatException("Unknown filter type.");
}
break;
this.ProcessDefilteredScanline(scanline, y, pixels);
default:
throw new ImageFormatException("Unknown filter type.");
Swap(ref scanline, ref previousScanline);
}
}
finally
{
ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(scanline);
}
}
/// <summary>
/// Decodes the raw interlaced pixel data row by row
/// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/>
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="pixels">The image pixel accessor.</param>
private void DecodeInterlacedPixelData<TColor, TPacked>(Stream compressedStream, PixelAccessor<TColor, TPacked> pixels)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
byte[] previousScanline = ArrayPool<byte>.Shared.Rent(this.bytesPerScanline);
byte[] scanline = ArrayPool<byte>.Shared.Rent(this.bytesPerScanline);
try
{
for (int pass = 0; pass < 7; pass++)
{
// Zero out the previousScanline, because the bytes that are rented from the arraypool may not be zero.
Array.Clear(previousScanline, 0, this.bytesPerScanline);
int y = Adam7FirstRow[pass];
int numColumns = this.ComputeColumnsAdam7(pass);
if (numColumns == 0)
{
// This pass contains no data; skip to next pass
continue;
}
int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1;
while (y < this.header.Height)
{
compressedStream.Read(scanline, 0, bytesPerInterlaceScanline);
FilterType filterType = (FilterType)scanline[0];
switch (filterType)
{
case FilterType.None:
NoneFilter.Decode(scanline);
break;
case FilterType.Sub:
SubFilter.Decode(scanline, bytesPerInterlaceScanline, this.bytesPerPixel);
break;
case FilterType.Up:
UpFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline);
previousScanline = defilteredScanline;
this.ProcessDefilteredScanline(defilteredScanline, y, pixels);
break;
case FilterType.Average:
AverageFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
break;
case FilterType.Paeth:
PaethFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel);
break;
default:
throw new ImageFormatException("Unknown filter type.");
}
this.ProcessInterlacedDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]);
Swap(ref scanline, ref previousScanline);
y += Adam7RowIncrement[pass];
}
}
}
finally
{
ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(scanline);
}
}
@ -343,12 +534,11 @@ namespace ImageSharp.Formats
switch (this.PngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
for (int x = 0; x < this.header.Width; x++)
{
int offset = 1 + (x * this.bytesPerPixel);
byte intensity = defilteredScanline[offset];
byte intensity = (byte)(newScanline1[x] * factor);
color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[x, row] = color;
}
@ -372,7 +562,7 @@ namespace ImageSharp.Formats
case PngColorType.Palette:
byte[] newScanline = defilteredScanline.ToArrayByBitsLength(this.header.BitDepth);
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
@ -392,6 +582,10 @@ namespace ImageSharp.Formats
byte b = this.palette[pixelOffset + 2];
color.PackFromBytes(r, g, b, a);
}
else
{
color.PackFromBytes(0, 0, 0, 0);
}
pixels[x, row] = color;
}
@ -449,6 +643,128 @@ namespace ImageSharp.Formats
}
}
/// <summary>
/// Processes the interlaced de-filtered scanline filling the image pixel data
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="row">The current image row.</param>
/// <param name="pixels">The image pixels</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TColor, TPacked>(byte[] defilteredScanline, int row, PixelAccessor<TColor, TPacked> pixels, int pixelOffset = 0, int increment = 1)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
TColor color = default(TColor);
switch (this.PngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
byte[] newScanline1 = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
byte intensity = (byte)(newScanline1[x - pixelOffset] * factor);
color.PackFromBytes(intensity, intensity, intensity, 255);
pixels[x, row] = color;
}
break;
case PngColorType.GrayscaleWithAlpha:
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
byte intensity = defilteredScanline[o];
byte alpha = defilteredScanline[o + this.bytesPerSample];
color.PackFromBytes(intensity, intensity, intensity, alpha);
pixels[x, row] = color;
}
break;
case PngColorType.Palette:
byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
{
// If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha
// channel and we should try to read it.
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
int index = newScanline[x - pixelOffset];
int offset = index * 3;
byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255;
if (a > 0)
{
byte r = this.palette[offset];
byte g = this.palette[offset + 1];
byte b = this.palette[offset + 2];
color.PackFromBytes(r, g, b, a);
}
else
{
color.PackFromBytes(0, 0, 0, 0);
}
pixels[x, row] = color;
}
}
else
{
for (int x = pixelOffset; x < this.header.Width; x += increment)
{
int index = newScanline[x - pixelOffset];
int offset = index * 3;
byte r = this.palette[offset];
byte g = this.palette[offset + 1];
byte b = this.palette[offset + 2];
color.PackFromBytes(r, g, b, 255);
pixels[x, row] = color;
}
}
break;
case PngColorType.Rgb:
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
byte r = defilteredScanline[o];
byte g = defilteredScanline[o + this.bytesPerSample];
byte b = defilteredScanline[o + (2 * this.bytesPerSample)];
color.PackFromBytes(r, g, b, 255);
pixels[x, row] = color;
}
break;
case PngColorType.RgbWithAlpha:
for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel)
{
byte r = defilteredScanline[o];
byte g = defilteredScanline[o + this.bytesPerSample];
byte b = defilteredScanline[o + (2 * this.bytesPerSample)];
byte a = defilteredScanline[o + (3 * this.bytesPerSample)];
color.PackFromBytes(r, g, b, a);
pixels[x, row] = color;
}
break;
}
}
/// <summary>
/// Reads a text chunk containing image properties from the data.
/// </summary>
@ -456,13 +772,14 @@ namespace ImageSharp.Formats
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to decode to.</param>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
private void ReadTextChunk<TColor, TPacked>(Image<TColor, TPacked> image, byte[] data)
/// <param name="length">The maximum length to read.</param>
private void ReadTextChunk<TColor, TPacked>(Image<TColor, TPacked> image, byte[] data, int length)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
int zeroIndex = 0;
for (int i = 0; i < data.Length; i++)
for (int i = 0; i < length; i++)
{
if (data[i] == 0)
{
@ -472,7 +789,7 @@ namespace ImageSharp.Formats
}
string name = Encoding.Unicode.GetString(data, 0, zeroIndex);
string value = Encoding.Unicode.GetString(data, zeroIndex + 1, data.Length - zeroIndex - 1);
string value = Encoding.Unicode.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
image.Properties.Add(new ImageProperty(name, value));
}
@ -495,7 +812,7 @@ namespace ImageSharp.Formats
this.header.ColorType = data[9];
this.header.CompressionMethod = data[10];
this.header.FilterMethod = data[11];
this.header.InterlaceMethod = data[12];
this.header.InterlaceMethod = (PngInterlaceMode)data[12];
}
/// <summary>
@ -521,10 +838,9 @@ namespace ImageSharp.Formats
throw new NotSupportedException("The png specification only defines 0 as filter method.");
}
if (this.header.InterlaceMethod != 0)
if (this.header.InterlaceMethod != PngInterlaceMode.None && this.header.InterlaceMethod != PngInterlaceMode.Adam7)
{
// TODO: Support interlacing
throw new NotSupportedException("Interlacing is not supported.");
throw new NotSupportedException("The png specification only defines 'None' and 'Adam7' as interlaced methods.");
}
this.PngColorType = (PngColorType)this.header.ColorType;
@ -539,13 +855,8 @@ namespace ImageSharp.Formats
private PngChunk ReadChunk()
{
PngChunk chunk = new PngChunk();
if (this.ReadChunkLength(chunk) == 0)
{
return null;
}
if (chunk.Length <= 0)
this.ReadChunkLength(chunk);
if (chunk.Length < 0)
{
return null;
}
@ -576,11 +887,11 @@ namespace ImageSharp.Formats
chunk.Crc = BitConverter.ToUInt32(this.crcBuffer, 0);
Crc32 crc = new Crc32();
crc.Update(this.chunkTypeBuffer);
crc.Update(chunk.Data);
this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer);
this.crc.Update(chunk.Data, 0, chunk.Length);
if (crc.Value != chunk.Crc)
if (this.crc.Value != chunk.Crc)
{
throw new ImageFormatException("CRC Error. PNG Image chunk is corrupt!");
}
@ -592,8 +903,8 @@ namespace ImageSharp.Formats
/// <param name="chunk">The chunk.</param>
private void ReadChunkData(PngChunk chunk)
{
// TODO: It might be possible to rent this but that could also lead to issues assigning the data to various properties
chunk.Data = new byte[chunk.Length];
// We rent the buffer here to return it afterwards in Decode()
chunk.Data = ArrayPool<byte>.Shared.Rent(chunk.Length);
this.currentStream.Read(chunk.Data, 0, chunk.Length);
}
@ -623,14 +934,11 @@ namespace ImageSharp.Formats
/// <summary>
/// Calculates the length of the given chunk.
/// </summary>
/// <param name="chunk">he chunk.</param>
/// <returns>
/// The <see cref="int"/> representing the chunk length.
/// </returns>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid.
/// </exception>
private int ReadChunkLength(PngChunk chunk)
private void ReadChunkLength(PngChunk chunk)
{
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
if (numBytes >= 1 && numBytes <= 3)
@ -638,11 +946,36 @@ namespace ImageSharp.Formats
throw new ImageFormatException("Image stream is not valid!");
}
if (numBytes <= 0)
{
chunk.Length = -1;
return;
}
this.chunkLengthBuffer.ReverseBytes();
chunk.Length = BitConverter.ToInt32(this.chunkLengthBuffer, 0);
}
return numBytes;
/// <summary>
/// Returns the correct number of columns for each interlaced pass.
/// </summary>
/// <param name="pass">Th current pass index</param>
/// <returns>The <see cref="int"/></returns>
private int ComputeColumnsAdam7(int pass)
{
int width = this.header.Width;
switch (pass)
{
case 0: return (width + 7) / 8;
case 1: return (width + 3) / 8;
case 2: return (width + 3) / 4;
case 3: return (width + 1) / 4;
case 4: return (width + 1) / 2;
case 5: return width / 2;
case 6: return width;
default: throw new ArgumentException($"Not a valid pass index: {pass}");
}
}
}
}
}

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

@ -13,9 +13,10 @@ namespace ImageSharp.Formats
using Quantizers;
using static ComparableExtensions;
/// <summary>
/// Performs the png encoding operation.
/// TODO: Perf. There's lots of array parsing and copying going on here. This should be unmanaged.
/// </summary>
internal sealed class PngEncoderCore
{
@ -34,6 +35,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly byte[] chunkDataBuffer = new byte[16];
/// <summary>
/// Reusable crc for validating chunks.
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// Contains the raw pixel data from an indexed image.
/// </summary>
@ -59,6 +65,26 @@ namespace ImageSharp.Formats
/// </summary>
private int bytesPerPixel;
/// <summary>
/// The buffer for the sub filter
/// </summary>
private byte[] sub;
/// <summary>
/// The buffer for the up filter
/// </summary>
private byte[] up;
/// <summary>
/// The buffer for the average filter
/// </summary>
private byte[] average;
/// <summary>
/// The buffer for the paeth filter
/// </summary>
private byte[] paeth;
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
@ -295,10 +321,10 @@ namespace ImageSharp.Formats
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
int bpp = this.bytesPerPixel;
for (int x = 0; x < this.width; x++)
// We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory.
using (PixelRow<TColor, TPacked> pixelRow = new PixelRow<TColor, TPacked>(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ))
{
pixels[x, row].ToBytes(rawScanline, x * this.bytesPerPixel, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ);
pixels.CopyTo(pixelRow, row);
}
}
@ -312,16 +338,16 @@ namespace ImageSharp.Formats
/// <param name="row">The row.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="rawScanline">The raw scanline.</param>
/// <param name="result">The resultant filtered scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline.</param>
private void EncodePixelRow<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result, int bytesPerScanline)
/// <param name="result">The filtered scanline result.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] EncodePixelRow<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
switch (this.PngColorType)
{
case PngColorType.Palette:
Buffer.BlockCopy(this.palettePixelData, row * bytesPerScanline, rawScanline, 0, bytesPerScanline);
Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length);
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
@ -332,7 +358,7 @@ namespace ImageSharp.Formats
break;
}
this.GetOptimalFilteredScanline(rawScanline, previousScanline, result, bytesPerScanline);
return this.GetOptimalFilteredScanline(rawScanline, previousScanline, result);
}
/// <summary>
@ -341,60 +367,65 @@ namespace ImageSharp.Formats
/// </summary>
/// <param name="rawScanline">The raw scanline</param>
/// <param name="previousScanline">The previous scanline</param>
/// <param name="result">The filtered scanline result</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
private void GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
/// <param name="result">The filtered scanline result.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result)
{
// Palette images don't compress well with adaptive filtering.
if (this.PngColorType == PngColorType.Palette)
{
NoneFilter.Encode(rawScanline, result, bytesPerScanline);
return;
NoneFilter.Encode(rawScanline, result);
return result;
}
Tuple<byte[], int>[] candidates = new Tuple<byte[], int>[4];
SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel);
int currentTotalVariation = this.CalculateTotalVariation(this.sub);
int lowestTotalVariation = currentTotalVariation;
result = this.sub;
byte[] sub = SubFilter.Encode(rawScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub));
UpFilter.Encode(rawScanline, previousScanline, this.up);
currentTotalVariation = this.CalculateTotalVariation(this.up);
byte[] up = UpFilter.Encode(rawScanline, previousScanline, result, bytesPerScanline);
candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up));
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = this.up;
}
byte[] average = AverageFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average));
AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel);
currentTotalVariation = this.CalculateTotalVariation(this.average);
byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth));
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = this.average;
}
int lowestTotalVariation = int.MaxValue;
int lowestTotalVariationIndex = 0;
PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel);
currentTotalVariation = this.CalculateTotalVariation(this.paeth);
for (int i = 0; i < candidates.Length; i++)
if (currentTotalVariation < lowestTotalVariation)
{
if (candidates[i].Item2 < lowestTotalVariation)
{
lowestTotalVariationIndex = i;
lowestTotalVariation = candidates[i].Item2;
}
result = this.paeth;
}
// ReSharper disable once RedundantAssignment
result = candidates[lowestTotalVariationIndex].Item1;
return result;
}
/// <summary>
/// Calculates the total variation of given byte array. Total variation is the sum of the absolute values of
/// neighbor differences.
/// </summary>
/// <param name="input">The scanline bytes</param>
/// <param name="scanline">The scanline bytes</param>
/// <returns>The <see cref="int"/></returns>
private int CalculateTotalVariation(byte[] input)
private int CalculateTotalVariation(byte[] scanline)
{
int totalVariation = 0;
for (int i = 1; i < input.Length; i++)
for (int i = 1; i < scanline.Length; i++)
{
totalVariation += Math.Abs(input[i] - input[i - 1]);
totalVariation += Math.Abs(scanline[i] - scanline[i - 1]);
}
return totalVariation;
@ -442,7 +473,7 @@ namespace ImageSharp.Formats
this.chunkDataBuffer[9] = header.ColorType;
this.chunkDataBuffer[10] = header.CompressionMethod;
this.chunkDataBuffer[11] = header.FilterMethod;
this.chunkDataBuffer[12] = header.InterlaceMethod;
this.chunkDataBuffer[12] = (byte)header.InterlaceMethod;
this.WriteChunk(stream, PngChunkTypes.Header, this.chunkDataBuffer, 0, 13);
}
@ -492,15 +523,6 @@ namespace ImageSharp.Formats
int alpha = bytes[3];
// Premultiply the color. This helps prevent banding.
// TODO: Vector<byte>?
if (alpha < 255 && alpha > this.Threshold)
{
bytes[0] = (byte)(bytes[0] * alpha).Clamp(0, 255);
bytes[1] = (byte)(bytes[1] * alpha).Clamp(0, 255);
bytes[2] = (byte)(bytes[2] * alpha).Clamp(0, 255);
}
colorTable[offset] = bytes[0];
colorTable[offset + 1] = bytes[1];
colorTable[offset + 2] = bytes[2];
@ -588,15 +610,18 @@ namespace ImageSharp.Formats
where TPacked : struct
{
int bytesPerScanline = this.width * this.bytesPerPixel;
byte[] previousScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
byte[] rawScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
byte[] previousScanline = new byte[bytesPerScanline];
byte[] rawScanline = new byte[bytesPerScanline];
int resultLength = bytesPerScanline + 1;
byte[] result = ArrayPool<byte>.Shared.Rent(resultLength);
byte[] result = new byte[resultLength];
// TODO: Clearing this array makes the visual tests work again when encoding multiple images in a row.
// The png analyser tool I use still cannot decompress the image though my own decoder, chome and edge browsers, and paint can. Twitter also cannot read the file.
// It's 2am now so I'm going to check in what I have and cry. :'(
Array.Clear(result, 0, resultLength);
if (this.PngColorType != PngColorType.Palette)
{
this.sub = new byte[resultLength];
this.up = new byte[resultLength];
this.average = new byte[resultLength];
this.paeth = new byte[resultLength];
}
byte[] buffer;
int bufferLength;
@ -608,26 +633,18 @@ namespace ImageSharp.Formats
{
for (int y = 0; y < this.height; y++)
{
this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result, bytesPerScanline);
deflateStream.Write(result, 0, resultLength);
deflateStream.Write(this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result), 0, resultLength);
// Do a bit of shuffling;
byte[] tmp = rawScanline;
rawScanline = previousScanline;
previousScanline = tmp;
Swap(ref rawScanline, ref previousScanline);
}
deflateStream.Flush();
buffer = memoryStream.ToArray();
bufferLength = buffer.Length;
}
buffer = memoryStream.ToArray();
bufferLength = buffer.Length;
}
finally
{
memoryStream?.Dispose();
ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(rawScanline);
ArrayPool<byte>.Shared.Return(result);
}
// Store the chunks in repeated 64k blocks.
@ -682,10 +699,8 @@ namespace ImageSharp.Formats
/// <param name="length">The of the data to write.</param>
private void WriteChunk(Stream stream, string type, byte[] data, int offset, int length)
{
// Chunk length
WriteInteger(stream, length);
// Chunk type
this.chunkTypeBuffer[0] = (byte)type[0];
this.chunkTypeBuffer[1] = (byte)type[1];
this.chunkTypeBuffer[2] = (byte)type[2];
@ -693,17 +708,20 @@ namespace ImageSharp.Formats
stream.Write(this.chunkTypeBuffer, 0, 4);
Crc32 crc32 = new Crc32();
crc32.Update(this.chunkTypeBuffer);
// Chunk data
if (data != null)
{
stream.Write(data, offset, length);
crc32.Update(data, offset, length);
}
WriteInteger(stream, (uint)crc32.Value);
this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer);
if (data != null && length > 0)
{
this.crc.Update(data, offset, length);
}
WriteInteger(stream, (uint)this.crc.Value);
}
}
}
}

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

@ -57,6 +57,6 @@ namespace ImageSharp.Formats
/// Indicates the transmission order of the image data.
/// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace).
/// </summary>
public byte InterlaceMethod { get; set; }
public PngInterlaceMode InterlaceMethod { get; set; }
}
}

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

@ -0,0 +1,23 @@
// <copyright file="PngInterlaceMode.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Provides enumeration of available PNG interlace modes.
/// </summary>
public enum PngInterlaceMode : byte
{
/// <summary>
/// Non interlaced
/// </summary>
None,
/// <summary>
/// Adam 7 interlacing.
/// </summary>
Adam7
}
}

10
src/ImageSharp/IO/EndianBitConverter.cs

@ -20,9 +20,11 @@ namespace ImageSharp.IO
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:ElementsMustAppearInTheCorrectOrder", Justification = "Reviewed. Suppression is OK here. Better readability.")]
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "Reviewed. Suppression is OK here. Better readability.")]
[SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:StaticElementsMustAppearBeforeInstanceElements", Justification = "Reviewed. Suppression is OK here. Better readability.")]
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:DoNotUseRegions", Justification = "Reviewed. Suppression is OK here. Better readability.")]
internal abstract class EndianBitConverter
{
#region Endianness of this converter
/// <summary>
/// Indicates the byte order ("endianness") in which data is converted using this class.
/// </summary>
@ -41,6 +43,7 @@ namespace ImageSharp.IO
#endregion
#region Factory properties
/// <summary>
/// The little-endian bit converter.
/// </summary>
@ -65,6 +68,7 @@ namespace ImageSharp.IO
#endregion
#region Double/primitive conversions
/// <summary>
/// Converts the specified double-precision floating point number to a
/// 64-bit signed integer. Note: the endianness of this converter does not
@ -115,6 +119,7 @@ namespace ImageSharp.IO
#endregion
#region To(PrimitiveType) conversions
/// <summary>
/// Returns a Boolean value converted from one byte at a specified position in a byte array.
/// </summary>
@ -279,6 +284,7 @@ namespace ImageSharp.IO
#endregion
#region ToString conversions
/// <summary>
/// Returns a String converted from the elements of a byte array.
/// </summary>
@ -326,6 +332,7 @@ namespace ImageSharp.IO
#endregion
#region Decimal conversions
/// <summary>
/// Returns a decimal value converted from sixteen bytes
/// at a specified position in a byte array.
@ -382,6 +389,7 @@ namespace ImageSharp.IO
#endregion
#region GetBytes conversions
/// <summary>
/// Returns an array with the given number of bytes formed
/// from the least significant bytes of the specified value.
@ -508,6 +516,7 @@ namespace ImageSharp.IO
#endregion
#region CopyBytes conversions
/// <summary>
/// Copies the given number of bytes from the least-specific
/// end of the specified value into the specified byte array, beginning
@ -669,6 +678,7 @@ namespace ImageSharp.IO
#endregion
#region Private struct used for Single/Int32 conversions
/// <summary>
/// Union used solely for the equivalent of DoubleToInt64Bits and vice versa.
/// </summary>

6
src/ImageSharp/Image.cs

@ -61,5 +61,11 @@ namespace ImageSharp
{
return new PixelAccessor(this);
}
/// <inheritdoc />
internal override ImageFrame<Color, uint> ToFrame()
{
return new ImageFrame(this);
}
}
}

67
src/ImageSharp/Image/Image.cs

@ -10,7 +10,9 @@ namespace ImageSharp
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Formats;
@ -245,7 +247,7 @@ namespace ImageSharp
/// <summary>
/// Returns a Base64 encoded string from the given image.
/// </summary>
/// <example>data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA==</example>
/// <example><see href="data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA=="/></example>
/// <returns>The <see cref="string"/></returns>
public string ToBase64String()
{
@ -257,6 +259,60 @@ namespace ImageSharp
}
}
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between unknown color formats.</param>
/// <typeparam name="TColor2">The pixel format.</typeparam>
/// <typeparam name="TPacked2">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>The <see cref="Image{TColor2, TPacked2}"/></returns>
public Image<TColor2, TPacked2> To<TColor2, TPacked2>(Func<Vector4, Vector4> scaleFunc = null)
where TColor2 : struct, IPackedPixel<TPacked2>
where TPacked2 : struct
{
scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TColor, TColor2>(scaleFunc);
Image<TColor2, TPacked2> target = new Image<TColor2, TPacked2>(this.Width, this.Height)
{
Quality = this.Quality,
FrameDelay = this.FrameDelay,
HorizontalResolution = this.HorizontalResolution,
VerticalResolution = this.VerticalResolution,
CurrentImageFormat = this.CurrentImageFormat,
RepeatCount = this.RepeatCount
};
using (PixelAccessor<TColor, TPacked> pixels = this.Lock())
using (PixelAccessor<TColor2, TPacked2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
TColor2 color = default(TColor2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
if (this.ExifProfile != null)
{
target.ExifProfile = new ExifProfile(this.ExifProfile);
}
foreach (ImageFrame<TColor, TPacked> frame in this.Frames)
{
target.Frames.Add(frame.To<TColor2, TPacked2>());
}
return target;
}
/// <summary>
/// Copies the properties from the other <see cref="Image{TColor, TPacked}"/>.
/// </summary>
@ -278,6 +334,15 @@ namespace ImageSharp
}
}
/// <summary>
/// Creates a new <see cref="ImageFrame{TColor,TPacked}"/> from this instance
/// </summary>
/// <returns>The <see cref="ImageFrame{TColor,TPacked}"/></returns>
internal virtual ImageFrame<TColor, TPacked> ToFrame()
{
return new ImageFrame<TColor, TPacked>(this);
}
/// <summary>
/// Loads the image from the given stream.
/// </summary>

65
src/ImageSharp/Image/ImageFrame.cs

@ -5,6 +5,10 @@
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Represents a single frame in a animation.
/// </summary>
@ -24,11 +28,9 @@ namespace ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TColor, TPacked}"/> class.
/// </summary>
/// <param name="frame">
/// The frame to create the frame from.
/// </param>
public ImageFrame(ImageFrame<TColor, TPacked> frame)
: base(frame)
/// <param name="image">The image to create the frame from.</param>
public ImageFrame(ImageBase<TColor, TPacked> image)
: base(image)
{
}
@ -37,5 +39,56 @@ namespace ImageSharp
{
return $"ImageFrame: {this.Width}x{this.Height}";
}
/// <summary>
/// Returns a copy of the image frame in the given pixel format.
/// </summary>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between unknown color formats.</param>
/// <typeparam name="TColor2">The pixel format.</typeparam>
/// <typeparam name="TPacked2">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>The <see cref="ImageFrame{TColor2, TPacked2}"/></returns>
public ImageFrame<TColor2, TPacked2> To<TColor2, TPacked2>(Func<Vector4, Vector4> scaleFunc = null)
where TColor2 : struct, IPackedPixel<TPacked2>
where TPacked2 : struct
{
scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TColor, TColor2>(scaleFunc);
ImageFrame<TColor2, TPacked2> target = new ImageFrame<TColor2, TPacked2>
{
Quality = this.Quality,
FrameDelay = this.FrameDelay
};
target.InitPixels(this.Width, this.Height);
using (PixelAccessor<TColor, TPacked> pixels = this.Lock())
using (PixelAccessor<TColor2, TPacked2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
TColor2 color = default(TColor2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
return target;
}
/// <summary>
/// Clones the current instance.
/// </summary>
/// <returns>The <see cref="ImageFrame{TColor, TPacked}"/></returns>
internal virtual ImageFrame<TColor, TPacked> Clone()
{
return new ImageFrame<TColor, TPacked>(this);
}
}
}
}

77
src/ImageSharp/Image/PixelAccessor.cs

@ -6,6 +6,7 @@
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -104,8 +105,19 @@ namespace ImageSharp
/// <returns>The <see typeparam="TColor"/> at the specified position.</returns>
public TColor this[int x, int y]
{
get { return Unsafe.Read<TColor>(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>())); }
set { Unsafe.Write(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>()), value); }
get
{
this.CheckCoordinates(x, y);
return Unsafe.Read<TColor>(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>()));
}
set
{
this.CheckCoordinates(x, y);
Unsafe.Write(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>()), value);
}
}
/// <summary>
@ -141,24 +153,25 @@ namespace ImageSharp
/// </summary>
/// <param name="row">The row.</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(PixelRow<TColor, TPacked> row, int targetY)
public void CopyFrom(PixelRow<TColor, TPacked> row, int targetY, int targetX = 0)
{
switch (row.ComponentOrder)
{
case ComponentOrder.ZYX:
this.CopyFromZYX(row, targetY, Math.Min(row.Width, this.Width));
this.CopyFromZYX(row, targetY, targetX, Math.Min(row.Width, this.Width));
break;
case ComponentOrder.ZYXW:
this.CopyFromZYXW(row, targetY, Math.Min(row.Width, this.Width));
this.CopyFromZYXW(row, targetY, targetX, Math.Min(row.Width, this.Width));
break;
case ComponentOrder.XYZ:
this.CopyFromXYZ(row, targetY, Math.Min(row.Width, this.Width));
this.CopyFromXYZ(row, targetY, targetX, Math.Min(row.Width, this.Width));
break;
case ComponentOrder.XYZW:
this.CopyFromXYZW(row, targetY, Math.Min(row.Width, this.Width));
this.CopyFromXYZW(row, targetY, targetX, Math.Min(row.Width, this.Width));
break;
default:
throw new NotSupportedException();
@ -223,16 +236,25 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
/// <summary>
/// Resets all the pixels to it's initial value.
/// </summary>
internal void Reset()
{
Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height));
}
/// <summary>
/// Copies from a row in <see cref="ComponentOrder.ZYX"/> format.
/// </summary>
/// <param name="row">The row.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param>
protected virtual void CopyFromZYX(PixelRow<TColor, TPacked> row, int targetY, int width)
protected virtual void CopyFromZYX(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY);
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
@ -252,11 +274,12 @@ namespace ImageSharp
/// </summary>
/// <param name="row">The row.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param>
protected virtual void CopyFromZYXW(PixelRow<TColor, TPacked> row, int targetY, int width)
protected virtual void CopyFromZYXW(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY);
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
@ -276,11 +299,12 @@ namespace ImageSharp
/// </summary>
/// <param name="row">The row.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param>
protected virtual void CopyFromXYZ(PixelRow<TColor, TPacked> row, int targetY, int width)
protected virtual void CopyFromXYZ(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY);
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
@ -300,11 +324,12 @@ namespace ImageSharp
/// </summary>
/// <param name="row">The row.</param>
/// <param name="targetY">The target row index.</param>
/// <param name="targetX">The target column index.</param>
/// <param name="width">The width.</param>
protected virtual void CopyFromXYZW(PixelRow<TColor, TPacked> row, int targetY, int width)
protected virtual void CopyFromXYZW(PixelRow<TColor, TPacked> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY);
byte* destination = this.GetRowPointer(targetY) + targetX;
TColor packed = default(TColor);
int size = Unsafe.SizeOf<TColor>();
@ -394,5 +419,27 @@ namespace ImageSharp
{
return this.pixelsBase + ((targetY * this.Width) * Unsafe.SizeOf<TColor>());
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <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 coordinates are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(int x, int y)
{
if (x < 0 || x >= this.Width)
{
throw new ArgumentOutOfRangeException(nameof(x), x, $"{x} is outwith the image bounds.");
}
if (y < 0 || y >= this.Height)
{
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds.");
}
}
}
}

35
src/ImageSharp/Image/PixelRow.cs

@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
@ -39,6 +40,32 @@ namespace ImageSharp
/// </remarks>
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="PixelRow{TColor,TPacked}"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="bytes">The bytes.</param>
/// <param name="componentOrder">The component order.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if <paramref name="bytes"></paramref> is the incorrect length.
/// </exception>
public PixelRow(int width, byte[] bytes, ComponentOrder componentOrder)
{
if (bytes.Length != width * GetComponentCount(componentOrder))
{
throw new ArgumentOutOfRangeException($"Invalid byte array length. Length {bytes.Length}; Should be {width * GetComponentCount(componentOrder)}.");
}
this.Width = width;
this.ComponentOrder = componentOrder;
this.Bytes = bytes;
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned);
// TODO: Why is Resharper warning us about an impure method call?
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.PixelBase = (byte*)this.dataPointer.ToPointer();
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelRow{TColor,TPacked}"/> class.
/// </summary>
@ -151,6 +178,14 @@ namespace ImageSharp
GC.SuppressFinalize(this);
}
/// <summary>
/// Resets the bytes of the array to it's initial value.
/// </summary>
internal void Reset()
{
Unsafe.InitBlock(this.PixelBase, 0, (uint)this.Bytes.Length);
}
/// <summary>
/// Gets component count for the given order.
/// </summary>

46
src/ImageSharp/ImageFrame.cs

@ -0,0 +1,46 @@
// <copyright file="ImageFrame.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Diagnostics;
/// <summary>
/// An optimized frame for the <see cref="Image"/> class.
/// </summary>
[DebuggerDisplay("ImageFrame: {Width}x{Height}")]
public class ImageFrame : ImageFrame<Color, uint>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame"/> class.
/// </summary>
public ImageFrame()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame"/> class.
/// </summary>
/// <param name="image">
/// The image to create the frame from.
/// </param>
public ImageFrame(ImageBase<Color, uint> image)
: base(image)
{
}
/// <inheritdoc />
public override PixelAccessor<Color, uint> Lock()
{
return new PixelAccessor(this);
}
/// <inheritdoc />
internal override ImageFrame<Color, uint> Clone()
{
return new ImageFrame(this);
}
}
}

20
src/ImageSharp/Numerics/Rectangle.cs

@ -213,6 +213,16 @@ namespace ImageSharp
return !left.Equals(right);
}
/// <summary>
/// Returns the center point of the given <see cref="Rectangle"/>
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns><see cref="Point"/></returns>
public static Point Center(Rectangle rectangle)
{
return new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
}
/// <summary>
/// Determines if the specfied point is contained within the rectangular region defined by
/// this <see cref="Rectangle"/>.
@ -229,16 +239,6 @@ namespace ImageSharp
&& y < this.Bottom;
}
/// <summary>
/// Returns the center point of the given <see cref="Rectangle"/>
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns><see cref="Point"/></returns>
public static Point Center(Rectangle rectangle)
{
return new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
}
/// <inheritdoc/>
public override int GetHashCode()
{

33
src/ImageSharp/PixelAccessor.cs

@ -5,7 +5,6 @@
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
/// <summary>
@ -23,10 +22,34 @@ namespace ImageSharp
}
/// <inheritdoc />
protected override void CopyFromZYX(PixelRow<Color, uint> row, int targetY, int width)
protected override void CopyFromXYZW(PixelRow<Color, uint> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY);
byte* destination = this.GetRowPointer(targetY) + targetX;
Unsafe.CopyBlock(destination, source, (uint)width * 4);
}
/// <inheritdoc />
protected override void CopyFromXYZ(PixelRow<Color, uint> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY) + targetX;
for (int x = 0; x < width; x++)
{
Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24));
source += 3;
destination += 4;
}
}
/// <inheritdoc />
protected override void CopyFromZYX(PixelRow<Color, uint> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY) + targetX;
for (int x = 0; x < width; x++)
{
@ -38,10 +61,10 @@ namespace ImageSharp
}
/// <inheritdoc />
protected override void CopyFromZYXW(PixelRow<Color, uint> row, int targetY, int width)
protected override void CopyFromZYXW(PixelRow<Color, uint> row, int targetY, int targetX, int width)
{
byte* source = row.PixelBase;
byte* destination = this.GetRowPointer(targetY);
byte* destination = this.GetRowPointer(targetY) + targetX;
for (int x = 0; x < width; x++)
{

9
src/ImageSharp/Profiles/Exif/ExifProfile.cs

@ -117,6 +117,9 @@ namespace ImageSharp
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>
/// The <see cref="Image{TColor,TPacked}"/>.
/// </returns>
public Image<TColor, TPacked> CreateThumbnail<TColor, TPacked>()
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
@ -143,6 +146,9 @@ namespace ImageSharp
/// Returns the value with the specified tag.
/// </summary>
/// <param name="tag">The tag of the EXIF value.</param>
/// <returns>
/// The <see cref="ExifValue"/>.
/// </returns>
public ExifValue GetValue(ExifTag tag)
{
foreach (ExifValue exifValue in this.Values)
@ -160,6 +166,9 @@ namespace ImageSharp
/// Removes the value with the specified tag.
/// </summary>
/// <param name="tag">The tag of the EXIF value.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public bool RemoveValue(ExifTag tag)
{
this.InitializeValues();

82
src/ImageSharp/Profiles/Exif/ExifReader.cs

@ -15,8 +15,6 @@ namespace ImageSharp
/// </summary>
internal sealed class ExifReader
{
private delegate TDataType ConverterMethod<TDataType>(byte[] data);
private readonly Collection<ExifTag> invalidTags = new Collection<ExifTag>();
private byte[] exifData;
private uint currentIndex;
@ -25,6 +23,13 @@ namespace ImageSharp
private uint gpsOffset;
private uint startIndex;
private delegate TDataType ConverterMethod<TDataType>(byte[] data);
/// <summary>
/// Gets the invalid tags.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the thumbnail length in the byte stream
/// </summary>
@ -112,10 +117,40 @@ namespace ImageSharp
return result;
}
/// <summary>
/// Gets the invalid tags.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, byte[] data, ConverterMethod<TDataType> converter)
{
int dataTypeSize = (int)ExifValue.GetSize(dataType);
int length = data.Length / dataTypeSize;
TDataType[] result = new TDataType[length];
byte[] buffer = new byte[dataTypeSize];
for (int i = 0; i < length; i++)
{
Array.Copy(data, i * dataTypeSize, buffer, 0, dataTypeSize);
result.SetValue(converter(buffer), i);
}
return result;
}
private static byte ToByte(byte[] data)
{
return data[0];
}
private static string ToString(byte[] data)
{
string result = Encoding.UTF8.GetString(data, 0, data.Length);
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
{
result = result.Substring(0, nullCharIndex);
}
return result;
}
/// <summary>
/// Adds the collection of EXIF values to the reader.
@ -369,29 +404,6 @@ namespace ImageSharp
}
}
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, byte[] data, ConverterMethod<TDataType> converter)
{
int dataTypeSize = (int)ExifValue.GetSize(dataType);
int length = data.Length / dataTypeSize;
TDataType[] result = new TDataType[length];
byte[] buffer = new byte[dataTypeSize];
for (int i = 0; i < length; i++)
{
Array.Copy(data, i * dataTypeSize, buffer, 0, dataTypeSize);
result.SetValue(converter(buffer), i);
}
return result;
}
private static byte ToByte(byte[] data)
{
return data[0];
}
private double ToDouble(byte[] data)
{
if (!this.ValidateArray(data, 8))
@ -432,18 +444,6 @@ namespace ImageSharp
return BitConverter.ToSingle(data, 0);
}
private static string ToString(byte[] data)
{
string result = Encoding.UTF8.GetString(data, 0, data.Length);
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
{
result = result.Substring(0, nullCharIndex);
}
return result;
}
private Rational ToRational(byte[] data)
{
if (!this.ValidateArray(data, 8, 4))

10
src/ImageSharp/Profiles/Exif/ExifTagDescriptionAttribute.cs

@ -28,6 +28,14 @@ namespace ImageSharp
this.description = description;
}
/// <summary>
/// Gets the tag description from any custom attributes.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="value">The value.</param>
/// <returns>
/// The <see cref="string"/>.
/// </returns>
public static string GetDescription(ExifTag tag, object value)
{
FieldInfo field = tag.GetType().GetTypeInfo().GetDeclaredField(tag.ToString());
@ -40,7 +48,7 @@ namespace ImageSharp
{
object attributeValue = customAttribute.ConstructorArguments[0].Value;
if (Equals(attributeValue, value))
if (object.Equals(attributeValue, value))
{
return (string)customAttribute.ConstructorArguments[1].Value;
}

292
src/ImageSharp/Profiles/Exif/ExifValue.cs

@ -10,10 +10,13 @@ namespace ImageSharp
using System.Text;
/// <summary>
/// A value of the exif profile.
/// Represent the value of the EXIF profile.
/// </summary>
public sealed class ExifValue : IEquatable<ExifValue>
{
/// <summary>
/// The exif value.
/// </summary>
private object exifValue;
/// <summary>
@ -41,6 +44,37 @@ namespace ImageSharp
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray;
if (dataType == ExifDataType.Ascii)
{
this.IsArray = false;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
: this(tag, dataType, isArray)
{
this.exifValue = value;
}
/// <summary>
/// Gets the data type of the exif value.
/// </summary>
@ -74,6 +108,7 @@ namespace ImageSharp
{
return this.exifValue;
}
set
{
this.CheckValue(value);
@ -82,31 +117,100 @@ namespace ImageSharp
}
/// <summary>
/// Determines whether the specified ExifValue instances are considered equal.
/// Gets a value indicating whether the EXIF value has a value.
/// </summary>
/// <param name="left">The first ExifValue to compare.</param>
/// <param name="right"> The second ExifValue to compare.</param>
/// <returns></returns>
public static bool operator ==(ExifValue left, ExifValue right)
internal bool HasValue
{
return Equals(left, right);
get
{
if (this.exifValue == null)
{
return false;
}
if (this.DataType == ExifDataType.Ascii)
{
return ((string)this.exifValue).Length > 0;
}
return true;
}
}
/// <summary>
/// Determines whether the specified ExifValue instances are not considered equal.
/// Gets the length of the EXIF value
/// </summary>
/// <param name="left">The first ExifValue to compare.</param>
/// <param name="right"> The second ExifValue to compare.</param>
/// <returns></returns>
public static bool operator !=(ExifValue left, ExifValue right)
internal int Length
{
get
{
if (this.exifValue == null)
{
return 4;
}
int size = (int)(GetSize(this.DataType) * this.NumberOfComponents);
return size < 4 ? 4 : size;
}
}
/// <summary>
/// Gets the number of components.
/// </summary>
internal int NumberOfComponents
{
return !Equals(left, right);
get
{
if (this.DataType == ExifDataType.Ascii)
{
return Encoding.UTF8.GetBytes((string)this.exifValue).Length;
}
if (this.IsArray)
{
return ((Array)this.exifValue).Length;
}
return 1;
}
}
/// <summary>
/// Determines whether the specified object is equal to the current exif value.
/// Compares two <see cref="ExifValue"/> objects for equality.
/// </summary>
/// <param name="obj">The object to compare this exif value with.</param>
/// <param name="left">
/// The <see cref="ExifValue"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="ExifValue"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(ExifValue left, ExifValue right)
{
return ExifValue.Equals(left, right);
}
/// <summary>
/// Compares two <see cref="ExifValue"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="ExifValue"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="ExifValue"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(ExifValue left, ExifValue right)
{
return !ExifValue.Equals(left, right);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
@ -117,10 +221,7 @@ namespace ImageSharp
return this.Equals(obj as ExifValue);
}
/// <summary>
/// Determines whether the specified exif value is equal to the current exif value.
/// </summary>
/// <param name="other">The exif value to compare this exif value with.</param>
/// <inheritdoc />
public bool Equals(ExifValue other)
{
if (ReferenceEquals(other, null))
@ -136,7 +237,7 @@ namespace ImageSharp
return
this.Tag == other.Tag &&
this.DataType == other.DataType &&
Equals(this.exifValue, other.exifValue);
object.Equals(this.exifValue, other.exifValue);
}
/// <inheritdoc/>
@ -173,75 +274,17 @@ namespace ImageSharp
return sb.ToString();
}
internal bool HasValue
{
get
{
if (this.exifValue == null)
{
return false;
}
if (this.DataType == ExifDataType.Ascii)
{
return ((string)this.exifValue).Length > 0;
}
return true;
}
}
internal int Length
{
get
{
if (this.exifValue == null)
{
return 4;
}
int size = (int)(GetSize(this.DataType) * this.NumberOfComponents);
return size < 4 ? 4 : size;
}
}
internal int NumberOfComponents
{
get
{
if (this.DataType == ExifDataType.Ascii)
{
return Encoding.UTF8.GetBytes((string)this.exifValue).Length;
}
if (this.IsArray)
{
return ((Array)this.exifValue).Length;
}
return 1;
}
}
internal ExifValue(ExifTag tag, ExifDataType dataType, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray;
if (dataType == ExifDataType.Ascii)
{
this.IsArray = false;
}
}
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
: this(tag, dataType, isArray)
{
this.exifValue = value;
}
/// <summary>
/// Creates a new <see cref="ExifValue"/>
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="value">The value.</param>
/// <returns>
/// The <see cref="ExifValue"/>.
/// </returns>
/// <exception cref="NotSupportedException">
/// Thrown if the tag is not supported.
/// </exception>
internal static ExifValue Create(ExifTag tag, object value)
{
Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag");
@ -549,6 +592,16 @@ namespace ImageSharp
return exifValue;
}
/// <summary>
/// Gets the size in bytes of the given data type.
/// </summary>
/// <param name="dataType">The data type.</param>
/// <returns>
/// The <see cref="uint"/>.
/// </returns>
/// <exception cref="NotSupportedException">
/// Thrown if the type is unsupported.
/// </exception>
internal static uint GetSize(ExifDataType dataType)
{
switch (dataType)
@ -574,6 +627,42 @@ namespace ImageSharp
}
}
/// <summary>
/// Returns an EXIF value with a numeric type for the given tag.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="type">The numeric type.</param>
/// <param name="isArray">Whether the value is an array.</param>
/// <returns>
/// The <see cref="ExifValue"/>.
/// </returns>
private static ExifValue CreateNumber(ExifTag tag, Type type, bool isArray)
{
if (type == null || type == typeof(ushort))
{
return new ExifValue(tag, ExifDataType.Short, isArray);
}
if (type == typeof(short))
{
return new ExifValue(tag, ExifDataType.SignedShort, isArray);
}
if (type == typeof(uint))
{
return new ExifValue(tag, ExifDataType.Long, isArray);
}
return new ExifValue(tag, ExifDataType.SignedLong, isArray);
}
/// <summary>
/// Checks the value type of the given object.
/// </summary>
/// <param name="value">The value to check.</param>
/// <exception cref="NotSupportedException">
/// Thrown if the object type is not supported.
/// </exception>
private void CheckValue(object value)
{
if (value == null)
@ -639,26 +728,11 @@ namespace ImageSharp
}
}
private static ExifValue CreateNumber(ExifTag tag, Type type, bool isArray)
{
if (type == null || type == typeof(ushort))
{
return new ExifValue(tag, ExifDataType.Short, isArray);
}
if (type == typeof(short))
{
return new ExifValue(tag, ExifDataType.SignedShort, isArray);
}
if (type == typeof(uint))
{
return new ExifValue(tag, ExifDataType.Long, isArray);
}
return new ExifValue(tag, ExifDataType.SignedLong, isArray);
}
/// <summary>
/// Converts the object value of this instance to its equivalent string representation
/// </summary>
/// <param name="value">The value</param>
/// <returns>The <see cref="string"/></returns>
private string ToString(object value)
{
string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, value);

55
src/ImageSharp/Profiles/Exif/ExifWriter.cs

@ -10,9 +10,20 @@ namespace ImageSharp
using System.Collections.ObjectModel;
using System.Text;
/// <summary>
/// Contains methods for writing EXIF metadata.
/// </summary>
internal sealed class ExifWriter
{
private static readonly ExifTag[] IfdTags = new ExifTag[127]
/// <summary>
/// The start index.
/// </summary>
private const int StartIndex = 6;
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
private static readonly ExifTag[] IfdTags =
{
ExifTag.SubfileType,
ExifTag.OldSubfileType,
@ -143,7 +154,10 @@ namespace ImageSharp
ExifTag.GDALNoData
};
private static readonly ExifTag[] ExifTags = new ExifTag[92]
/// <summary>
/// The collection of Exif tags
/// </summary>
private static readonly ExifTag[] ExifTags =
{
ExifTag.ExposureTime,
ExifTag.FNumber,
@ -239,7 +253,10 @@ namespace ImageSharp
ExifTag.LensSerialNumber
};
private static readonly ExifTag[] GPSTags = new ExifTag[31]
/// <summary>
/// The collection of GPS tags
/// </summary>
private static readonly ExifTag[] GPSTags =
{
ExifTag.GPSVersionID,
ExifTag.GPSLatitudeRef,
@ -274,8 +291,9 @@ namespace ImageSharp
ExifTag.GPSDifferential
};
private const int StartIndex = 6;
/// <summary>
/// Which parts will be written.
/// </summary>
private ExifParts allowedParts;
private Collection<ExifValue> values;
private Collection<int> dataOffsets;
@ -283,6 +301,11 @@ namespace ImageSharp
private Collection<int> exifIndexes;
private Collection<int> gpsIndexes;
/// <summary>
/// Initializes a new instance of the <see cref="ExifWriter"/> class.
/// </summary>
/// <param name="values">The values.</param>
/// <param name="allowedParts">The allowed parts.</param>
public ExifWriter(Collection<ExifValue> values, ExifParts allowedParts)
{
this.values = values;
@ -292,9 +315,15 @@ namespace ImageSharp
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, GPSTags);
}
/// <summary>
/// Returns the EXIF data.
/// </summary>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetData()
{
uint length = 0;
uint length;
int exifIndex = -1;
int gpsIndex = -1;
@ -379,6 +408,13 @@ namespace ImageSharp
return result;
}
private static int Write(byte[] source, byte[] destination, int offset)
{
Buffer.BlockCopy(source, 0, destination, offset, source.Length);
return offset + source.Length;
}
private int GetIndex(Collection<int> indexes, ExifTag tag)
{
foreach (int index in indexes)
@ -443,13 +479,6 @@ namespace ImageSharp
return length;
}
private static int Write(byte[] source, byte[] destination, int offset)
{
Buffer.BlockCopy(source, 0, destination, offset, source.Length);
return offset + source.Length;
}
private int WriteArray(ExifValue value, byte[] destination, int offset)
{
if (value.DataType == ExifDataType.Ascii)

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

@ -6,7 +6,7 @@
namespace ImageSharp.Quantizers
{
using System;
using System.Collections.Generic;
using System.Buffers;
using System.Numerics;
using System.Threading.Tasks;
@ -24,10 +24,8 @@ namespace ImageSharp.Quantizers
/// <see href="https://github.com/JeremyAnsel/JeremyAnsel.ColorQuant"/>
/// </para>
/// <para>
/// Algorithm: Greedy orthogonal bipartition of RGB space for variance
/// minimization aided by inclusion-exclusion tricks.
/// For speed no nearest neighbor search is done. Slightly
/// better performance can be expected by more sophisticated
/// Algorithm: Greedy orthogonal bipartition of RGB space for variance minimization aided by inclusion-exclusion tricks.
/// For speed no nearest neighbor search is done. Slightly better performance can be expected by more sophisticated
/// but more expensive versions.
/// </para>
/// </remarks>
@ -40,7 +38,7 @@ namespace ImageSharp.Quantizers
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.001f;
private const float Epsilon = 1e-5F;
/// <summary>
/// The index bits.
@ -325,17 +323,18 @@ namespace ImageSharp.Quantizers
/// <param name="pixels">The pixel accessor.</param>
private void Build3DHistogram(PixelAccessor<TColor, TPacked> pixels)
{
byte[] rgba = new byte[4];
for (int y = 0; y < pixels.Height; y++)
{
for (int x = 0; x < pixels.Width; x++)
{
// Colors are expected in r->g->b->a format
Color color = new Color(pixels[x, y].ToVector4());
pixels[x, y].ToBytes(rgba, 0, ComponentOrder.XYZW);
byte r = color.R;
byte g = color.G;
byte b = color.B;
byte a = color.A;
byte r = rgba[0];
byte g = rgba[1];
byte b = rgba[2];
byte a = rgba[3];
int inr = r >> (8 - IndexBits);
int ing = g >> (8 - IndexBits);
@ -723,7 +722,7 @@ namespace ImageSharp.Quantizers
/// <returns>The result.</returns>
private QuantizedImage<TColor, TPacked> GenerateResult(PixelAccessor<TColor, TPacked> imagePixels, int colorCount, Box[] cube)
{
List<TColor> pallette = new List<TColor>();
TColor[] pallette = new TColor[colorCount];
byte[] pixels = new byte[imagePixels.Width * imagePixels.Height];
int width = imagePixels.Width;
int height = imagePixels.Height;
@ -743,11 +742,7 @@ namespace ImageSharp.Quantizers
TColor color = default(TColor);
color.PackFromVector4(new Vector4(r, g, b, a) / 255F);
pallette.Add(color);
}
else
{
pallette.Add(default(TColor));
pallette[k] = color;
}
}
@ -757,21 +752,25 @@ namespace ImageSharp.Quantizers
Bootstrapper.Instance.ParallelOptions,
y =>
{
byte[] rgba = ArrayPool<byte>.Shared.Rent(4);
for (int x = 0; x < width; x++)
{
// Expected order r->g->b->a
Color color = new Color(imagePixels[x, y].ToVector4());
int r = color.R >> (8 - IndexBits);
int g = color.G >> (8 - IndexBits);
int b = color.B >> (8 - IndexBits);
int a = color.A >> (8 - IndexAlphaBits);
imagePixels[x, y].ToBytes(rgba, 0, ComponentOrder.XYZW);
int r = rgba[0] >> (8 - IndexBits);
int g = rgba[1] >> (8 - IndexBits);
int b = rgba[2] >> (8 - IndexBits);
int a = rgba[3] >> (8 - IndexAlphaBits);
int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1);
pixels[(y * width) + x] = this.tag[ind];
}
ArrayPool<byte>.Shared.Return(rgba);
});
return new QuantizedImage<TColor, TPacked>(width, height, pallette.ToArray(), pixels);
return new QuantizedImage<TColor, TPacked>(width, height, pallette, pixels);
}
}
}

5
src/ImageSharp/Samplers/Processors/CropProcessor.cs

@ -24,6 +24,11 @@ namespace ImageSharp.Processors
int sourceX = sourceRectangle.X;
int sourceY = sourceRectangle.Y;
Guard.MustBeGreaterThanOrEqualTo(startX, sourceX, nameof(targetRectangle));
Guard.MustBeGreaterThanOrEqualTo(startY, sourceY, nameof(targetRectangle));
Guard.MustBeLessThanOrEqualTo(endX, sourceRectangle.Right, nameof(targetRectangle));
Guard.MustBeLessThanOrEqualTo(endY, sourceRectangle.Bottom, nameof(targetRectangle));
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock())
{

34
src/ImageSharp/Samplers/Processors/RotateProcessor.cs

@ -33,23 +33,6 @@ namespace ImageSharp.Processors
/// </summary>
public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, 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)
{
return;
}
this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand)
{
CreateNewTarget(target, sourceRectangle, this.processMatrix);
}
}
/// <inheritdoc/>
public override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
@ -83,6 +66,23 @@ namespace ImageSharp.Processors
}
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, 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)
{
return;
}
this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand)
{
CreateNewTarget(target, sourceRectangle, this.processMatrix);
}
}
/// <summary>
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
/// </summary>

48
src/ImageSharp/project.json

@ -28,30 +28,40 @@
"additionalArguments": [ "/additionalfile:stylecop.json" ]
},
"dependencies": {
"System.Collections": "4.0.11",
"System.Diagnostics.Debug": "4.0.11",
"System.Diagnostics.Tools": "4.0.1",
"System.IO": "4.1.0",
"System.IO.Compression": "4.1.0",
"System.Linq": "4.1.0",
"System.Numerics.Vectors": "4.1.1",
"System.ObjectModel": "4.0.12",
"System.Resources.ResourceManager": "4.0.1",
"System.Runtime.CompilerServices.Unsafe": "4.0.0",
"System.Runtime.Extensions": "4.1.0",
"System.Runtime.InteropServices": "4.1.0",
"System.Runtime.Numerics": "4.0.1",
"System.Text.Encoding.Extensions": "4.0.11",
"System.Threading": "4.0.11",
"System.Threading.Tasks": "4.0.11",
"System.Threading.Tasks.Parallel": "4.0.1",
"StyleCop.Analyzers": {
"version": "1.0.0",
"type": "build"
},
"System.Buffers": "4.0.0"
"System.Buffers": "4.0.0",
"System.Numerics.Vectors": "4.1.1",
"System.Runtime.CompilerServices.Unsafe": "4.0.0"
},
"frameworks": {
"netstandard1.1": {}
"netstandard1.1": {
"dependencies": {
"System.Collections": "4.0.11",
"System.Diagnostics.Debug": "4.0.11",
"System.Diagnostics.Tools": "4.0.1",
"System.IO": "4.1.0",
"System.IO.Compression": "4.1.0",
"System.Linq": "4.1.0",
"System.ObjectModel": "4.0.12",
"System.Resources.ResourceManager": "4.0.1",
"System.Runtime.Extensions": "4.1.0",
"System.Runtime.InteropServices": "4.1.0",
"System.Runtime.Numerics": "4.0.1",
"System.Text.Encoding.Extensions": "4.0.11",
"System.Threading": "4.0.11",
"System.Threading.Tasks": "4.0.11",
"System.Threading.Tasks.Parallel": "4.0.1"
}
},
"net45": {
"dependencies": {
},
"frameworkAssemblies": {
"System.Runtime": ""
}
}
}
}

69
src/ImageSharp46/Bootstrapper.cs

@ -1,69 +0,0 @@
// <copyright file="Bootstrapper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Formats;
/// <summary>
/// Provides initialization code which allows extending the library.
/// </summary>
public class Bootstrapper
{
/// <summary>
/// A new instance Initializes a new instance of the <see cref="Bootstrapper"/> class.
/// with lazy initialization.
/// </summary>
private static readonly Lazy<Bootstrapper> Lazy = new Lazy<Bootstrapper>(() => new Bootstrapper());
/// <summary>
/// The default list of supported <see cref="IImageFormat"/>
/// </summary>
private readonly List<IImageFormat> imageFormats;
/// <summary>
/// Prevents a default instance of the <see cref="Bootstrapper"/> class from being created.
/// </summary>
private Bootstrapper()
{
this.imageFormats = new List<IImageFormat>
{
new BmpFormat(),
new JpegFormat(),
new PngFormat(),
new GifFormat()
};
}
/// <summary>
/// Gets the current bootstrapper instance.
/// </summary>
public static Bootstrapper Instance => Lazy.Value;
/// <summary>
/// Gets the collection of supported <see cref="IImageFormat"/>
/// </summary>
public IReadOnlyCollection<IImageFormat> ImageFormats => new ReadOnlyCollection<IImageFormat>(this.imageFormats);
/// <summary>
/// Gets or sets the global parallel options for processing tasks in parallel.
/// </summary>
public ParallelOptions ParallelOptions { get; set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
/// <summary>
/// Adds a new <see cref="IImageFormat"/> to the collection of supported image formats.
/// </summary>
/// <param name="format">The new format to add.</param>
public void AddImageFormat(IImageFormat format)
{
this.imageFormats.Add(format);
}
}
}

408
src/ImageSharp46/Colors/Color.cs

@ -1,408 +0,0 @@
// <copyright file="Color.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Globalization;
using System.Numerics;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public partial struct Color : IPackedPixel<uint>, IEquatable<Color>
{
private const int RedShift = 0;
private const int GreenShift = 8;
private const int BlueShift = 16;
private const int AlphaShift = 24;
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5f);
/// <summary>
/// The packed value.
/// </summary>
private uint packedValue;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Color(byte r, byte g, byte b, byte a = 255)
{
this.packedValue = Pack(r, g, b, a);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="hex">
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
public Color(string hex)
{
Guard.NotNullOrEmpty(hex, nameof(hex));
hex = ToRgbaHex(hex);
if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out this.packedValue))
{
throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
}
// Order parsed from hex string will be backwards, so reverse it.
this.packedValue = Pack(this.A, this.B, this.G, this.R);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Color(float r, float g, float b, float a = 1)
{
this.packedValue = Pack(r, g, b, a);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
public Color(Vector3 vector)
{
this.packedValue = Pack(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
public Color(Vector4 vector)
{
this.packedValue = Pack(ref vector);
}
/// <summary>
/// Gets or sets the red component.
/// </summary>
public byte R
{
get
{
return (byte)(this.packedValue >> RedShift);
}
set
{
this.packedValue = this.packedValue & 0xFFFFFF00 | (uint)value << RedShift;
}
}
/// <summary>
/// Gets or sets the green component.
/// </summary>
public byte G
{
get
{
return (byte)(this.packedValue >> GreenShift);
}
set
{
this.packedValue = this.packedValue & 0xFFFF00FF | (uint)value << GreenShift;
}
}
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public byte B
{
get
{
return (byte)(this.packedValue >> BlueShift);
}
set
{
this.packedValue = this.packedValue & 0xFF00FFFF | (uint)value << BlueShift;
}
}
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public byte A
{
get
{
return (byte)(this.packedValue >> AlphaShift);
}
set
{
this.packedValue = this.packedValue & 0x00FFFFFF | (uint)value << AlphaShift;
}
}
/// <inheritdoc/>
public uint PackedValue
{
get
{
return this.packedValue;
}
set
{
this.packedValue = value;
}
}
/// <summary>
/// Compares two <see cref="Color"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Color"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Color"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Color left, Color right)
{
return left.packedValue == right.packedValue;
}
/// <summary>
/// Compares two <see cref="Color"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Color"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Color"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Color left, Color right)
{
return left.packedValue != right.packedValue;
}
/// <summary>
/// Creates a new instance of the <see cref="Color"/> struct.
/// </summary>
/// <param name="hex">
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color FromHex(string hex)
{
return new Color(hex);
}
/// <inheritdoc/>
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.packedValue = Pack(x, y, z, w);
}
/// <summary>
/// Converts the value of this instance to a hexadecimal string.
/// </summary>
/// <returns>A hexadecimal string representation of the value.</returns>
public string ToHex()
{
uint hexOrder = Pack(this.A, this.B, this.G, this.R);
return hexOrder.ToString("X8");
}
/// <inheritdoc/>
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
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();
}
}
/// <inheritdoc/>
public void PackFromVector4(Vector4 vector)
{
this.packedValue = Pack(ref vector);
}
/// <inheritdoc/>
public Vector4 ToVector4()
{
return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is Color) && this.Equals((Color)obj);
}
/// <inheritdoc/>
public bool Equals(Color other)
{
return this.packedValue == other.packedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.packedValue.GetHashCode();
}
/// <summary>
/// Packs a <see cref="Vector4"/> into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(ref Vector4 vector)
{
vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One);
vector *= MaxBytes;
vector += Half;
return (uint)(((byte)vector.X << RedShift)
| ((byte)vector.Y << GreenShift)
| ((byte)vector.Z << BlueShift)
| (byte)vector.W << AlphaShift);
}
/// <summary>
/// Packs a <see cref="Vector3"/> into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(ref Vector3 vector)
{
Vector4 value = new Vector4(vector, 1);
return Pack(ref value);
}
/// <summary>
/// Packs the four floats into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/></returns>
private static uint Pack(float x, float y, float z, float w)
{
Vector4 value = new Vector4(x, y, z, w);
return Pack(ref value);
}
/// <summary>
/// Packs the four floats into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/></returns>
private static uint Pack(byte x, byte y, byte z, byte w)
{
return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift);
}
/// <summary>
/// Converts the specified hex value to an rrggbbaa hex value.
/// </summary>
/// <param name="hex">The hex value to convert.</param>
/// <returns>
/// A rrggbbaa hex value.
/// </returns>
private static string ToRgbaHex(string hex)
{
hex = hex.StartsWith("#") ? hex.Substring(1) : hex;
if (hex.Length == 8)
{
return hex;
}
if (hex.Length == 6)
{
return hex + "FF";
}
if (hex.Length < 3 || hex.Length > 4)
{
return null;
}
string red = char.ToString(hex[0]);
string green = char.ToString(hex[1]);
string blue = char.ToString(hex[2]);
string alpha = hex.Length == 3 ? "F" : char.ToString(hex[3]);
return red + red + green + green + blue + blue + alpha + alpha;
}
}
}

179
src/ImageSharp46/Colors/ColorConstants.cs

@ -1,179 +0,0 @@
// <copyright file="ColorConstants.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Collections.Generic;
/// <summary>
/// Provides useful color definitions.
/// </summary>
public static class ColorConstants
{
/// <summary>
/// Provides a lazy, one time method of returning the colors.
/// </summary>
private static readonly Lazy<Color[]> SafeColors = new Lazy<Color[]>(GetWebSafeColors);
/// <summary>
/// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4.
/// </summary>
public static Color[] WebSafeColors => SafeColors.Value;
/// <summary>
/// Returns an array of web safe colors.
/// </summary>
/// <returns></returns>
private static Color[] GetWebSafeColors()
{
return new List<Color>
{
Color.AliceBlue,
Color.AntiqueWhite,
Color.Aqua,
Color.Aquamarine,
Color.Azure,
Color.Beige,
Color.Bisque,
Color.Black,
Color.BlanchedAlmond,
Color.Blue,
Color.BlueViolet,
Color.Brown,
Color.BurlyWood,
Color.CadetBlue,
Color.Chartreuse,
Color.Chocolate,
Color.Coral,
Color.CornflowerBlue,
Color.Cornsilk,
Color.Crimson,
Color.Cyan,
Color.DarkBlue,
Color.DarkCyan,
Color.DarkGoldenrod,
Color.DarkGray,
Color.DarkGreen,
Color.DarkKhaki,
Color.DarkMagenta,
Color.DarkOliveGreen,
Color.DarkOrange,
Color.DarkOrchid,
Color.DarkRed,
Color.DarkSalmon,
Color.DarkSeaGreen,
Color.DarkSlateBlue,
Color.DarkSlateGray,
Color.DarkTurquoise,
Color.DarkViolet,
Color.DeepPink,
Color.DeepSkyBlue,
Color.DimGray,
Color.DodgerBlue,
Color.Firebrick,
Color.FloralWhite,
Color.ForestGreen,
Color.Fuchsia,
Color.Gainsboro,
Color.GhostWhite,
Color.Gold,
Color.Goldenrod,
Color.Gray,
Color.Green,
Color.GreenYellow,
Color.Honeydew,
Color.HotPink,
Color.IndianRed,
Color.Indigo,
Color.Ivory,
Color.Khaki,
Color.Lavender,
Color.LavenderBlush,
Color.LawnGreen,
Color.LemonChiffon,
Color.LightBlue,
Color.LightCoral,
Color.LightCyan,
Color.LightGoldenrodYellow,
Color.LightGray,
Color.LightGreen,
Color.LightPink,
Color.LightSalmon,
Color.LightSeaGreen,
Color.LightSkyBlue,
Color.LightSlateGray,
Color.LightSteelBlue,
Color.LightYellow,
Color.Lime,
Color.LimeGreen,
Color.Linen,
Color.Magenta,
Color.Maroon,
Color.MediumAquamarine,
Color.MediumBlue,
Color.MediumOrchid,
Color.MediumPurple,
Color.MediumSeaGreen,
Color.MediumSlateBlue,
Color.MediumSpringGreen,
Color.MediumTurquoise,
Color.MediumVioletRed,
Color.MidnightBlue,
Color.MintCream,
Color.MistyRose,
Color.Moccasin,
Color.NavajoWhite,
Color.Navy,
Color.OldLace,
Color.Olive,
Color.OliveDrab,
Color.Orange,
Color.OrangeRed,
Color.Orchid,
Color.PaleGoldenrod,
Color.PaleGreen,
Color.PaleTurquoise,
Color.PaleVioletRed,
Color.PapayaWhip,
Color.PeachPuff,
Color.Peru,
Color.Pink,
Color.Plum,
Color.PowderBlue,
Color.Purple,
Color.RebeccaPurple,
Color.Red,
Color.RosyBrown,
Color.RoyalBlue,
Color.SaddleBrown,
Color.Salmon,
Color.SandyBrown,
Color.SeaGreen,
Color.SeaShell,
Color.Sienna,
Color.Silver,
Color.SkyBlue,
Color.SlateBlue,
Color.SlateGray,
Color.Snow,
Color.SpringGreen,
Color.SteelBlue,
Color.Tan,
Color.Teal,
Color.Thistle,
Color.Tomato,
Color.Transparent,
Color.Turquoise,
Color.Violet,
Color.Wheat,
Color.White,
Color.WhiteSmoke,
Color.Yellow,
Color.YellowGreen
}.ToArray();
}
}
}

728
src/ImageSharp46/Colors/ColorDefinitions.cs

@ -1,728 +0,0 @@
// <copyright file="ColorDefinitions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public partial struct Color
{
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F0F8FF.
/// </summary>
public static readonly Color AliceBlue = new Color(240, 248, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FAEBD7.
/// </summary>
public static readonly Color AntiqueWhite = new Color(250, 235, 215, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00FFFF.
/// </summary>
public static readonly Color Aqua = new Color(0, 255, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #7FFFD4.
/// </summary>
public static readonly Color Aquamarine = new Color(127, 255, 212, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F0FFFF.
/// </summary>
public static readonly Color Azure = new Color(240, 255, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F5F5DC.
/// </summary>
public static readonly Color Beige = new Color(245, 245, 220, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFE4C4.
/// </summary>
public static readonly Color Bisque = new Color(255, 228, 196, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #000000.
/// </summary>
public static readonly Color Black = new Color(0, 0, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFEBCD.
/// </summary>
public static readonly Color BlanchedAlmond = new Color(255, 235, 205, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #0000FF.
/// </summary>
public static readonly Color Blue = new Color(0, 0, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #8A2BE2.
/// </summary>
public static readonly Color BlueViolet = new Color(138, 43, 226, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #A52A2A.
/// </summary>
public static readonly Color Brown = new Color(165, 42, 42, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DEB887.
/// </summary>
public static readonly Color BurlyWood = new Color(222, 184, 135, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #5F9EA0.
/// </summary>
public static readonly Color CadetBlue = new Color(95, 158, 160, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #7FFF00.
/// </summary>
public static readonly Color Chartreuse = new Color(127, 255, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #D2691E.
/// </summary>
public static readonly Color Chocolate = new Color(210, 105, 30, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF7F50.
/// </summary>
public static readonly Color Coral = new Color(255, 127, 80, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #6495ED.
/// </summary>
public static readonly Color CornflowerBlue = new Color(100, 149, 237, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFF8DC.
/// </summary>
public static readonly Color Cornsilk = new Color(255, 248, 220, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DC143C.
/// </summary>
public static readonly Color Crimson = new Color(220, 20, 60, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00FFFF.
/// </summary>
public static readonly Color Cyan = new Color(0, 255, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00008B.
/// </summary>
public static readonly Color DarkBlue = new Color(0, 0, 139, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #008B8B.
/// </summary>
public static readonly Color DarkCyan = new Color(0, 139, 139, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #B8860B.
/// </summary>
public static readonly Color DarkGoldenrod = new Color(184, 134, 11, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #A9A9A9.
/// </summary>
public static readonly Color DarkGray = new Color(169, 169, 169, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #006400.
/// </summary>
public static readonly Color DarkGreen = new Color(0, 100, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #BDB76B.
/// </summary>
public static readonly Color DarkKhaki = new Color(189, 183, 107, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #8B008B.
/// </summary>
public static readonly Color DarkMagenta = new Color(139, 0, 139, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #556B2F.
/// </summary>
public static readonly Color DarkOliveGreen = new Color(85, 107, 47, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF8C00.
/// </summary>
public static readonly Color DarkOrange = new Color(255, 140, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #9932CC.
/// </summary>
public static readonly Color DarkOrchid = new Color(153, 50, 204, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #8B0000.
/// </summary>
public static readonly Color DarkRed = new Color(139, 0, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #E9967A.
/// </summary>
public static readonly Color DarkSalmon = new Color(233, 150, 122, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #8FBC8B.
/// </summary>
public static readonly Color DarkSeaGreen = new Color(143, 188, 139, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #483D8B.
/// </summary>
public static readonly Color DarkSlateBlue = new Color(72, 61, 139, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #2F4F4F.
/// </summary>
public static readonly Color DarkSlateGray = new Color(47, 79, 79, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00CED1.
/// </summary>
public static readonly Color DarkTurquoise = new Color(0, 206, 209, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #9400D3.
/// </summary>
public static readonly Color DarkViolet = new Color(148, 0, 211, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF1493.
/// </summary>
public static readonly Color DeepPink = new Color(255, 20, 147, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00BFFF.
/// </summary>
public static readonly Color DeepSkyBlue = new Color(0, 191, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #696969.
/// </summary>
public static readonly Color DimGray = new Color(105, 105, 105, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #1E90FF.
/// </summary>
public static readonly Color DodgerBlue = new Color(30, 144, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #B22222.
/// </summary>
public static readonly Color Firebrick = new Color(178, 34, 34, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFAF0.
/// </summary>
public static readonly Color FloralWhite = new Color(255, 250, 240, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #228B22.
/// </summary>
public static readonly Color ForestGreen = new Color(34, 139, 34, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF00FF.
/// </summary>
public static readonly Color Fuchsia = new Color(255, 0, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DCDCDC.
/// </summary>
public static readonly Color Gainsboro = new Color(220, 220, 220, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F8F8FF.
/// </summary>
public static readonly Color GhostWhite = new Color(248, 248, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFD700.
/// </summary>
public static readonly Color Gold = new Color(255, 215, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DAA520.
/// </summary>
public static readonly Color Goldenrod = new Color(218, 165, 32, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #808080.
/// </summary>
public static readonly Color Gray = new Color(128, 128, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #008000.
/// </summary>
public static readonly Color Green = new Color(0, 128, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #ADFF2F.
/// </summary>
public static readonly Color GreenYellow = new Color(173, 255, 47, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F0FFF0.
/// </summary>
public static readonly Color Honeydew = new Color(240, 255, 240, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF69B4.
/// </summary>
public static readonly Color HotPink = new Color(255, 105, 180, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #CD5C5C.
/// </summary>
public static readonly Color IndianRed = new Color(205, 92, 92, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #4B0082.
/// </summary>
public static readonly Color Indigo = new Color(75, 0, 130, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFFF0.
/// </summary>
public static readonly Color Ivory = new Color(255, 255, 240, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F0E68C.
/// </summary>
public static readonly Color Khaki = new Color(240, 230, 140, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #E6E6FA.
/// </summary>
public static readonly Color Lavender = new Color(230, 230, 250, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFF0F5.
/// </summary>
public static readonly Color LavenderBlush = new Color(255, 240, 245, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #7CFC00.
/// </summary>
public static readonly Color LawnGreen = new Color(124, 252, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFACD.
/// </summary>
public static readonly Color LemonChiffon = new Color(255, 250, 205, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #ADD8E6.
/// </summary>
public static readonly Color LightBlue = new Color(173, 216, 230, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F08080.
/// </summary>
public static readonly Color LightCoral = new Color(240, 128, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #E0FFFF.
/// </summary>
public static readonly Color LightCyan = new Color(224, 255, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FAFAD2.
/// </summary>
public static readonly Color LightGoldenrodYellow = new Color(250, 250, 210, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #D3D3D3.
/// </summary>
public static readonly Color LightGray = new Color(211, 211, 211, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #90EE90.
/// </summary>
public static readonly Color LightGreen = new Color(144, 238, 144, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFB6C1.
/// </summary>
public static readonly Color LightPink = new Color(255, 182, 193, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFA07A.
/// </summary>
public static readonly Color LightSalmon = new Color(255, 160, 122, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #20B2AA.
/// </summary>
public static readonly Color LightSeaGreen = new Color(32, 178, 170, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #87CEFA.
/// </summary>
public static readonly Color LightSkyBlue = new Color(135, 206, 250, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #778899.
/// </summary>
public static readonly Color LightSlateGray = new Color(119, 136, 153, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #B0C4DE.
/// </summary>
public static readonly Color LightSteelBlue = new Color(176, 196, 222, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFFE0.
/// </summary>
public static readonly Color LightYellow = new Color(255, 255, 224, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00FF00.
/// </summary>
public static readonly Color Lime = new Color(0, 255, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #32CD32.
/// </summary>
public static readonly Color LimeGreen = new Color(50, 205, 50, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FAF0E6.
/// </summary>
public static readonly Color Linen = new Color(250, 240, 230, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF00FF.
/// </summary>
public static readonly Color Magenta = new Color(255, 0, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #800000.
/// </summary>
public static readonly Color Maroon = new Color(128, 0, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #66CDAA.
/// </summary>
public static readonly Color MediumAquamarine = new Color(102, 205, 170, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #0000CD.
/// </summary>
public static readonly Color MediumBlue = new Color(0, 0, 205, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #BA55D3.
/// </summary>
public static readonly Color MediumOrchid = new Color(186, 85, 211, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #9370DB.
/// </summary>
public static readonly Color MediumPurple = new Color(147, 112, 219, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #3CB371.
/// </summary>
public static readonly Color MediumSeaGreen = new Color(60, 179, 113, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #7B68EE.
/// </summary>
public static readonly Color MediumSlateBlue = new Color(123, 104, 238, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00FA9A.
/// </summary>
public static readonly Color MediumSpringGreen = new Color(0, 250, 154, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #48D1CC.
/// </summary>
public static readonly Color MediumTurquoise = new Color(72, 209, 204, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #C71585.
/// </summary>
public static readonly Color MediumVioletRed = new Color(199, 21, 133, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #191970.
/// </summary>
public static readonly Color MidnightBlue = new Color(25, 25, 112, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F5FFFA.
/// </summary>
public static readonly Color MintCream = new Color(245, 255, 250, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFE4E1.
/// </summary>
public static readonly Color MistyRose = new Color(255, 228, 225, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFE4B5.
/// </summary>
public static readonly Color Moccasin = new Color(255, 228, 181, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFDEAD.
/// </summary>
public static readonly Color NavajoWhite = new Color(255, 222, 173, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #000080.
/// </summary>
public static readonly Color Navy = new Color(0, 0, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FDF5E6.
/// </summary>
public static readonly Color OldLace = new Color(253, 245, 230, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #808000.
/// </summary>
public static readonly Color Olive = new Color(128, 128, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #6B8E23.
/// </summary>
public static readonly Color OliveDrab = new Color(107, 142, 35, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFA500.
/// </summary>
public static readonly Color Orange = new Color(255, 165, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF4500.
/// </summary>
public static readonly Color OrangeRed = new Color(255, 69, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DA70D6.
/// </summary>
public static readonly Color Orchid = new Color(218, 112, 214, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #EEE8AA.
/// </summary>
public static readonly Color PaleGoldenrod = new Color(238, 232, 170, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #98FB98.
/// </summary>
public static readonly Color PaleGreen = new Color(152, 251, 152, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #AFEEEE.
/// </summary>
public static readonly Color PaleTurquoise = new Color(175, 238, 238, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DB7093.
/// </summary>
public static readonly Color PaleVioletRed = new Color(219, 112, 147, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFEFD5.
/// </summary>
public static readonly Color PapayaWhip = new Color(255, 239, 213, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFDAB9.
/// </summary>
public static readonly Color PeachPuff = new Color(255, 218, 185, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #CD853F.
/// </summary>
public static readonly Color Peru = new Color(205, 133, 63, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFC0CB.
/// </summary>
public static readonly Color Pink = new Color(255, 192, 203, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #DDA0DD.
/// </summary>
public static readonly Color Plum = new Color(221, 160, 221, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #B0E0E6.
/// </summary>
public static readonly Color PowderBlue = new Color(176, 224, 230, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #800080.
/// </summary>
public static readonly Color Purple = new Color(128, 0, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #0.
/// </summary>
public static readonly Color RebeccaPurple = new Color(102, 51, 153, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF0000.
/// </summary>
public static readonly Color Red = new Color(255, 0, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #BC8F8F.
/// </summary>
public static readonly Color RosyBrown = new Color(188, 143, 143, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #4169E1.
/// </summary>
public static readonly Color RoyalBlue = new Color(65, 105, 225, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #8B4513.
/// </summary>
public static readonly Color SaddleBrown = new Color(139, 69, 19, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FA8072.
/// </summary>
public static readonly Color Salmon = new Color(250, 128, 114, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F4A460.
/// </summary>
public static readonly Color SandyBrown = new Color(244, 164, 96, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #2E8B57.
/// </summary>
public static readonly Color SeaGreen = new Color(46, 139, 87, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFF5EE.
/// </summary>
public static readonly Color SeaShell = new Color(255, 245, 238, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #A0522D.
/// </summary>
public static readonly Color Sienna = new Color(160, 82, 45, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #C0C0C0.
/// </summary>
public static readonly Color Silver = new Color(192, 192, 192, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #87CEEB.
/// </summary>
public static readonly Color SkyBlue = new Color(135, 206, 235, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #6A5ACD.
/// </summary>
public static readonly Color SlateBlue = new Color(106, 90, 205, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #708090.
/// </summary>
public static readonly Color SlateGray = new Color(112, 128, 144, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFAFA.
/// </summary>
public static readonly Color Snow = new Color(255, 250, 250, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #00FF7F.
/// </summary>
public static readonly Color SpringGreen = new Color(0, 255, 127, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #4682B4.
/// </summary>
public static readonly Color SteelBlue = new Color(70, 130, 180, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #D2B48C.
/// </summary>
public static readonly Color Tan = new Color(210, 180, 140, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #008080.
/// </summary>
public static readonly Color Teal = new Color(0, 128, 128, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #D8BFD8.
/// </summary>
public static readonly Color Thistle = new Color(216, 191, 216, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FF6347.
/// </summary>
public static readonly Color Tomato = new Color(255, 99, 71, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFFFF.
/// </summary>
public static readonly Color Transparent = new Color(255, 255, 255, 0);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #40E0D0.
/// </summary>
public static readonly Color Turquoise = new Color(64, 224, 208, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #EE82EE.
/// </summary>
public static readonly Color Violet = new Color(238, 130, 238, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F5DEB3.
/// </summary>
public static readonly Color Wheat = new Color(245, 222, 179, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFFFF.
/// </summary>
public static readonly Color White = new Color(255, 255, 255, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #F5F5F5.
/// </summary>
public static readonly Color WhiteSmoke = new Color(245, 245, 245, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #FFFF00.
/// </summary>
public static readonly Color Yellow = new Color(255, 255, 0, 255);
/// <summary>
/// Represents a <see cref="Color"/> matching the W3C definition that has an hex value of #9ACD32.
/// </summary>
public static readonly Color YellowGreen = new Color(154, 205, 50, 255);
}
}

74
src/ImageSharp46/Colors/ColorTransforms.cs

@ -1,74 +0,0 @@
// <copyright file="ColorTransforms.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </remarks>
public partial struct Color
{
/// <summary>
/// Blends two colors by multiplication.
/// <remarks>
/// The source color is multiplied by the destination color and replaces the destination.
/// The resultant color is always at least as dark as either the source or destination color.
/// Multiplying any color with black results in black. Multiplying any color with white preserves the
/// original color.
/// </remarks>
/// </summary>
/// <param name="source">The source color.</param>
/// <param name="destination">The destination color.</param>
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
public static Color Multiply(Color source, Color destination)
{
if (destination == Color.Black)
{
return Color.Black;
}
if (destination == Color.White)
{
return source;
}
// TODO: This will use less memory than using Vector4
// but we should test speed vs memory to see which is best balance.
byte r = (byte)(source.R * destination.R).Clamp(0, 255);
byte g = (byte)(source.G * destination.G).Clamp(0, 255);
byte b = (byte)(source.B * destination.B).Clamp(0, 255);
byte a = (byte)(source.A * destination.A).Clamp(0, 255);
return new Color(r, g, b, a);
}
/// <summary>
/// Linearly interpolates from one color to another based on the given weighting.
/// </summary>
/// <param name="from">The first color value.</param>
/// <param name="to">The second color value.</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns>
/// The <see cref="Color"/>
/// </returns>
public static Color Lerp(Color from, Color to, float amount)
{
return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount));
}
}
}

292
src/ImageSharp46/Colors/ColorspaceTransforms.cs

@ -1,292 +0,0 @@
// <copyright file="ColorspaceTransforms.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
/// as it avoids the need to create new values for modification operations.
/// </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"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Color(Bgra32 color)
{
return new Color(color.R, color.G, color.B, color.A);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Cmyk"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="cmykColor">The instance of <see cref="Cmyk"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
public static implicit operator Color(Cmyk cmykColor)
{
float r = (1 - cmykColor.C) * (1 - cmykColor.K);
float g = (1 - cmykColor.M) * (1 - cmykColor.K);
float b = (1 - cmykColor.Y) * (1 - cmykColor.K);
return new Color(r, g, b, 1);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="YCbCr"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="YCbCr"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
public static implicit operator Color(YCbCr color)
{
byte y = color.Y;
int cb = color.Cb - 128;
int 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);
byte b = (byte)(y + (1.772F * cb)).Clamp(0, 255);
return new Color(r, g, b);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="CieXyz"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="CieXyz"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
public static implicit operator Color(CieXyz color)
{
float x = color.X / 100F;
float y = color.Y / 100F;
float z = color.Z / 100F;
// Then XYZ to RGB (multiplication by 100 was done above already)
float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F);
float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
Vector4 vector = new Vector4(r, g, b, 1).Compress();
return new Color(vector);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsv"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Hsv"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
public static implicit operator Color(Hsv color)
{
float s = color.S;
float v = color.V;
if (Math.Abs(s) < Epsilon)
{
return new Color(v, v, v, 1);
}
float h = (Math.Abs(color.H - 360) < Epsilon) ? 0 : color.H / 60;
int i = (int)Math.Truncate(h);
float f = h - i;
float p = v * (1.0F - s);
float q = v * (1.0F - (s * f));
float t = v * (1.0F - (s * (1.0F - f)));
float r, g, b;
switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default:
r = v;
g = p;
b = q;
break;
}
return new Color(r, g, b, 1);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Hsl"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Hsl"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
public static implicit operator Color(Hsl color)
{
float rangedH = color.H / 360F;
float r = 0;
float g = 0;
float b = 0;
float s = color.S;
float l = color.L;
if (Math.Abs(l) > Epsilon)
{
if (Math.Abs(s) < Epsilon)
{
r = g = b = l;
}
else
{
float temp2 = (l < 0.5f) ? l * (1f + s) : l + s - (l * s);
float temp1 = (2f * l) - temp2;
r = GetColorComponent(temp1, temp2, rangedH + 0.3333333F);
g = GetColorComponent(temp1, temp2, rangedH);
b = GetColorComponent(temp1, temp2, rangedH - 0.3333333F);
}
}
return new Color(r, g, b, 1);
}
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="CieLab"/> to a
/// <see cref="Color"/>.
/// </summary>
/// <param name="cieLabColor">The instance of <see cref="CieLab"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Color"/>.
/// </returns>
public static implicit operator Color(CieLab cieLabColor)
{
// First convert back to XYZ...
float y = (cieLabColor.L + 16F) / 116F;
float x = (cieLabColor.A / 500F) + y;
float z = y - (cieLabColor.B / 200F);
float x3 = x * x * x;
float y3 = y * y * y;
float z3 = z * z * z;
x = x3 > 0.008856F ? x3 : (x - 0.137931F) / 7.787F;
y = (cieLabColor.L > 7.999625F) ? y3 : (cieLabColor.L / 903.3F);
z = (z3 > 0.008856F) ? z3 : (z - 0.137931F) / 7.787F;
x *= 0.95047F;
z *= 1.08883F;
// Then XYZ to RGB (multiplication by 100 was done above already)
float r = (x * 3.2406F) + (y * -1.5372F) + (z * -0.4986F);
float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F);
float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F);
return new Color(new Vector4(r, g, b, 1F).Compress());
}
/// <summary>
/// Gets the color component from the given values.
/// </summary>
/// <param name="first">The first value.</param>
/// <param name="second">The second value.</param>
/// <param name="third">The third value.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float GetColorComponent(float first, float second, float third)
{
third = MoveIntoRange(third);
if (third < 0.1666667F)
{
return first + ((second - first) * 6.0f * third);
}
if (third < 0.5)
{
return second;
}
if (third < 0.6666667F)
{
return first + ((second - first) * (0.6666667F - third) * 6.0f);
}
return first;
}
/// <summary>
/// Moves the specific value within the acceptable range for
/// conversion.
/// <remarks>Used for converting <see cref="Hsl"/> colors to this type.</remarks>
/// </summary>
/// <param name="value">The value to shift.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float MoveIntoRange(float value)
{
if (value < 0.0)
{
value += 1.0f;
}
else if (value > 1.0)
{
value -= 1.0f;
}
return value;
}
}
}

167
src/ImageSharp46/Colors/Colorspaces/Bgra32.cs

@ -1,167 +0,0 @@
// <copyright file="Bgra32.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an BGRA (blue, green, red, alpha) color.
/// </summary>
public struct Bgra32 : IEquatable<Bgra32>
{
/// <summary>
/// Represents a 32 bit <see cref="Bgra32"/> that has B, G, R, and A values set to zero.
/// </summary>
public static readonly Bgra32 Empty = default(Bgra32);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private Vector4 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
/// </summary>
/// <param name="b">The blue component of this <see cref="Bgra32"/>.</param>
/// <param name="g">The green component of this <see cref="Bgra32"/>.</param>
/// <param name="r">The red component of this <see cref="Bgra32"/>.</param>
/// <param name="a">The alpha component of this <see cref="Bgra32"/>.</param>
public Bgra32(byte b, byte g, byte r, byte a = 255)
: this()
{
this.backingVector = Vector4.Clamp(new Vector4(b, g, r, a), Vector4.Zero, new Vector4(255));
}
/// <summary>
/// Gets the blue component of the color
/// </summary>
public byte B => (byte)this.backingVector.X;
/// <summary>
/// Gets the green component of the color
/// </summary>
public byte G => (byte)this.backingVector.Y;
/// <summary>
/// Gets the red component of the color
/// </summary>
public byte R => (byte)this.backingVector.Z;
/// <summary>
/// Gets the alpha component of the color
/// </summary>
public byte A => (byte)this.backingVector.W;
/// <summary>
/// Gets the <see cref="Bgra32"/> integer representation of the color.
/// </summary>
public int Bgra => (this.R << 16) | (this.G << 8) | (this.B << 0) | (this.A << 24);
/// <summary>
/// Gets a value indicating whether this <see cref="Bgra32"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Bgra32"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Bgra32"/>.
/// </returns>
public static implicit operator Bgra32(Color color)
{
return new Bgra32(color.B, color.G, color.R, color.A);
}
/// <summary>
/// Compares two <see cref="Bgra32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Bgra32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Bgra32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgra32 left, Bgra32 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Bgra32"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Bgra32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Bgra32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgra32 left, Bgra32 right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Bgra32)
{
Bgra32 color = (Bgra32)obj;
return this.backingVector == color.backingVector;
}
return false;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Bgra32 [ Empty ]";
}
return $"Bgra32 [ B={this.B}, G={this.G}, R={this.R}, A={this.A} ]";
}
/// <inheritdoc/>
public bool Equals(Bgra32 other)
{
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();
}
}

192
src/ImageSharp46/Colors/Colorspaces/CieLab.cs

@ -1,192 +0,0 @@
// <copyright file="CieLab.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an CIE LAB 1976 color.
/// <see href="https://en.wikipedia.org/wiki/Lab_color_space"/>
/// </summary>
public struct CieLab : IEquatable<CieLab>, IAlmostEquatable<CieLab, float>
{
/// <summary>
/// Represents a <see cref="CieLab"/> that has L, A, B values set to zero.
/// </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;
/// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct.
/// </summary>
/// <param name="l">The lightness dimension.</param>
/// <param name="a">The a (green - magenta) component.</param>
/// <param name="b">The b (blue - yellow) component.</param>
public CieLab(float l, float a, float b)
: this()
{
this.backingVector = Vector3.Clamp(new Vector3(l, a, b), new Vector3(0, -100, -100), new Vector3(100));
}
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public float L => this.backingVector.X;
/// <summary>
/// Gets the a color component.
/// <remarks>Negative is green, positive magenta.</remarks>
/// </summary>
public float A => this.backingVector.Y;
/// <summary>
/// Gets the b color component.
/// <remarks>Negative is blue, positive is yellow</remarks>
/// </summary>
public float B => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="CieLab"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="CieLab"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="CieLab"/>.
/// </returns>
public static implicit operator CieLab(Color color)
{
// First convert to CIE XYZ
Vector4 vector = color.ToVector4().Expand();
float x = (vector.X * 0.4124F) + (vector.Y * 0.3576F) + (vector.Z * 0.1805F);
float y = (vector.X * 0.2126F) + (vector.Y * 0.7152F) + (vector.Z * 0.0722F);
float z = (vector.X * 0.0193F) + (vector.Y * 0.1192F) + (vector.Z * 0.9505F);
// Now to LAB
x /= 0.95047F;
//y /= 1F;
z /= 1.08883F;
x = x > 0.008856F ? (float)Math.Pow(x, 0.3333333F) : ((903.3F * x) + 16F) / 116F;
y = y > 0.008856F ? (float)Math.Pow(y, 0.3333333F) : ((903.3F * y) + 16F) / 116F;
z = z > 0.008856F ? (float)Math.Pow(z, 0.3333333F) : ((903.3F * z) + 16F) / 116F;
float l = Math.Max(0, (116F * y) - 16F);
float a = 500F * (x - y);
float b = 200F * (y - z);
return new CieLab(l, a, b);
}
/// <summary>
/// Compares two <see cref="CieLab"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(CieLab left, CieLab right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="CieLab"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="CieLab"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieLab"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(CieLab left, CieLab right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "CieLab [Empty]";
}
return $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is CieLab)
{
return this.Equals((CieLab)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(CieLab other)
{
return this.AlmostEquals(other, Epsilon);
}
/// <inheritdoc/>
public bool AlmostEquals(CieLab other, float precision)
{
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& 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();
}
}

184
src/ImageSharp46/Colors/Colorspaces/CieXyz.cs

@ -1,184 +0,0 @@
// <copyright file="CieXyz.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an CIE 1931 color
/// <see href="https://en.wikipedia.org/wiki/CIE_1931_color_space"/>
/// </summary>
public struct CieXyz : IEquatable<CieXyz>, IAlmostEquatable<CieXyz, float>
{
/// <summary>
/// Represents a <see cref="CieXyz"/> that has Y, Cb, and Cr values set to zero.
/// </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;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
/// </summary>
/// <param name="y">The y luminance component.</param>
/// <param name="x">X is a mix (a linear combination) of cone response curves chosen to be nonnegative</param>
/// <param name="z">Z is quasi-equal to blue stimulation, or the S cone of the human eye.</param>
public CieXyz(float x, float y, float z)
: this()
{
// Not clamping as documentation about this space seems to indicate "usual" ranges
this.backingVector = new Vector3(x, y, z);
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 380 and 780.</remarks>
/// </summary>
public float X => this.backingVector.X;
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 380 and 780.</remarks>
/// </summary>
public float Y => this.backingVector.Y;
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 380 and 780.</remarks>
/// </summary>
public float Z => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="CieXyz"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="CieXyz"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="CieXyz"/>.
/// </returns>
public static implicit operator CieXyz(Color color)
{
Vector4 vector = color.ToVector4().Expand();
float x = (vector.X * 0.4124F) + (vector.Y * 0.3576F) + (vector.Z * 0.1805F);
float y = (vector.X * 0.2126F) + (vector.Y * 0.7152F) + (vector.Z * 0.0722F);
float z = (vector.X * 0.0193F) + (vector.Y * 0.1192F) + (vector.Z * 0.9505F);
x *= 100F;
y *= 100F;
z *= 100F;
return new CieXyz(x, y, z);
}
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(CieXyz left, CieXyz right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="CieXyz"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="CieXyz"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(CieXyz left, CieXyz right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "CieXyz [ Empty ]";
}
return $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is CieXyz)
{
return this.Equals((CieXyz)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(CieXyz other)
{
return this.AlmostEquals(other, Epsilon);
}
/// <inheritdoc/>
public bool AlmostEquals(CieXyz other, float precision)
{
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& 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();
}
}

195
src/ImageSharp46/Colors/Colorspaces/Cmyk.cs

@ -1,195 +0,0 @@
// <copyright file="Cmyk.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents an CMYK (cyan, magenta, yellow, keyline) color.
/// </summary>
public struct Cmyk : IEquatable<Cmyk>, IAlmostEquatable<Cmyk, float>
{
/// <summary>
/// Represents a <see cref="Cmyk"/> that has C, M, Y, and K values set to zero.
/// </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;
/// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// </summary>
/// <param name="c">The cyan component.</param>
/// <param name="m">The magenta component.</param>
/// <param name="y">The yellow component.</param>
/// <param name="k">The keyline black component.</param>
public Cmyk(float c, float m, float y, float k)
: this()
{
this.backingVector = Vector4.Clamp(new Vector4(c, m, y, k), Vector4.Zero, Vector4.One);
}
/// <summary>
/// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float C => this.backingVector.X;
/// <summary>
/// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float M => this.backingVector.Y;
/// <summary>
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float Y => this.backingVector.Z;
/// <summary>
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float K => this.backingVector.W;
/// <summary>
/// Gets a value indicating whether this <see cref="Cmyk"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Cmyk"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Bgra32"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="Cmyk"/>.
/// </returns>
public static implicit operator Cmyk(Color color)
{
float c = 1f - (color.R / 255F);
float m = 1f - (color.G / 255F);
float y = 1f - (color.B / 255F);
float k = Math.Min(c, Math.Min(m, y));
if (Math.Abs(k - 1.0f) <= Epsilon)
{
return new Cmyk(0, 0, 0, 1);
}
c = (c - k) / (1 - k);
m = (m - k) / (1 - k);
y = (y - k) / (1 - k);
return new Cmyk(c, m, y, k);
}
/// <summary>
/// Compares two <see cref="Cmyk"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Cmyk"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Cmyk"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Cmyk left, Cmyk right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Cmyk"/> objects for inequality
/// </summary>
/// <param name="left">
/// The <see cref="Cmyk"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Cmyk"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Cmyk left, Cmyk right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Cmyk [Empty]";
}
return $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Cmyk)
{
return this.Equals((Cmyk)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(Cmyk other)
{
return this.AlmostEquals(other, Epsilon);
}
/// <inheritdoc/>
public bool AlmostEquals(Cmyk other, float precision)
{
Vector4 result = Vector4.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& result.Y < precision
&& 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();
}
}

213
src/ImageSharp46/Colors/Colorspaces/Hsl.cs

@ -1,213 +0,0 @@
// <copyright file="Hsl.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents a Hsl (hue, saturation, lightness) color.
/// </summary>
public struct Hsl : IEquatable<Hsl>, IAlmostEquatable<Hsl, float>
{
/// <summary>
/// Represents a <see cref="Hsl"/> that has H, S, and L values set to zero.
/// </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;
/// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct.
/// </summary>
/// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param>
/// <param name="l">The l value (lightness) component.</param>
public Hsl(float h, float s, float l)
{
this.backingVector = Vector3.Clamp(new Vector3(h, s, l), Vector3.Zero, new Vector3(360, 1, 1));
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H => this.backingVector.X;
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S => this.backingVector.Y;
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float L => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="Hsl"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Hsl"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Hsl"/>.
/// </returns>
public static implicit operator Hsl(Color color)
{
float r = color.R / 255F;
float g = color.G / 255F;
float b = color.B / 255F;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float l = (max + min) / 2;
if (Math.Abs(chroma) < Epsilon)
{
return new Hsl(0, s, l);
}
if (Math.Abs(r - max) < Epsilon)
{
h = (g - b) / chroma;
}
else if (Math.Abs(g - max) < Epsilon)
{
h = 2 + ((b - r) / chroma);
}
else if (Math.Abs(b - max) < Epsilon)
{
h = 4 + ((r - g) / chroma);
}
h *= 60;
if (h < 0.0)
{
h += 360;
}
if (l <= .5f)
{
s = chroma / (max + min);
}
else
{
s = chroma / (2 - chroma);
}
return new Hsl(h, s, l);
}
/// <summary>
/// Compares two <see cref="Hsl"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Hsl left, Hsl right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Hsl"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Hsl left, Hsl right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Hsl [ Empty ]";
}
return $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Hsl)
{
return this.Equals((Hsl)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(Hsl other)
{
return this.AlmostEquals(other, Epsilon);
}
/// <inheritdoc/>
public bool AlmostEquals(Hsl other, float precision)
{
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& 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();
}
}

206
src/ImageSharp46/Colors/Colorspaces/Hsv.cs

@ -1,206 +0,0 @@
// <copyright file="Hsv.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
using System.Numerics;
/// <summary>
/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness).
/// </summary>
public struct Hsv : IEquatable<Hsv>, IAlmostEquatable<Hsv, float>
{
/// <summary>
/// Represents a <see cref="Hsv"/> that has H, S, and V values set to zero.
/// </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;
/// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> struct.
/// </summary>
/// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param>
/// <param name="v">The v value (brightness) component.</param>
public Hsv(float h, float s, float v)
{
this.backingVector = Vector3.Clamp(new Vector3(h, s, v), Vector3.Zero, new Vector3(360, 1, 1));
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H => this.backingVector.X;
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S => this.backingVector.Y;
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float V => this.backingVector.Z;
/// <summary>
/// Gets a value indicating whether this <see cref="Hsv"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="Hsv"/>.
/// </summary>
/// <param name="color">The instance of <see cref="Color"/> to convert.</param>
/// <returns>
/// An instance of <see cref="Hsv"/>.
/// </returns>
public static implicit operator Hsv(Color color)
{
float r = color.R / 255F;
float g = color.G / 255F;
float b = color.B / 255F;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
float chroma = max - min;
float h = 0;
float s = 0;
float v = max;
if (Math.Abs(chroma) < Epsilon)
{
return new Hsv(0, s, v);
}
if (Math.Abs(r - max) < Epsilon)
{
h = (g - b) / chroma;
}
else if (Math.Abs(g - max) < Epsilon)
{
h = 2 + ((b - r) / chroma);
}
else if (Math.Abs(b - max) < Epsilon)
{
h = 4 + ((r - g) / chroma);
}
h *= 60;
if (h < 0.0)
{
h += 360;
}
s = chroma / v;
return new Hsv(h, s, v);
}
/// <summary>
/// Compares two <see cref="Hsv"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Hsv left, Hsv right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Hsv"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsv"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsv"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Hsv left, Hsv right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return GetHashCode(this);
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "Hsv [ Empty ]";
}
return $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is Hsv)
{
return this.Equals((Hsv)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(Hsv other)
{
return this.AlmostEquals(other, Epsilon);
}
/// <inheritdoc/>
public bool AlmostEquals(Hsv other, float precision)
{
Vector3 result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X < precision
&& 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();
}
}

30
src/ImageSharp46/Colors/Colorspaces/IAlmostEquatable.cs

@ -1,30 +0,0 @@
// <copyright file="IAlmostEquatable.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
/// <summary>
/// Defines a generalized method that a value type or class implements to create
/// a type-specific method for determining approximate equality of instances.
/// </summary>
/// <typeparam name="TColor">The type of objects to compare.</typeparam>
/// <typeparam name="TPrecision">The object specifying the type to specify precision with.</typeparam>
public interface IAlmostEquatable<in TColor, in TPrecision>
where TPrecision : struct, IComparable<TPrecision>
{
/// <summary>
/// Indicates whether the current object is equal to another object of the same type
/// when compared to the specified precision level.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <param name="precision">The object specifying the level of precision.</param>
/// <returns>
/// true if the current object is equal to the other parameter; otherwise, false.
/// </returns>
bool AlmostEquals(TColor other, TPrecision precision);
}
}

157
src/ImageSharp46/Colors/Colorspaces/YCbCr.cs

@ -1,157 +0,0 @@
// <copyright file="YCbCr.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.ComponentModel;
/// <summary>
/// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the full range standard used in digital imaging systems.
/// <see href="http://en.wikipedia.org/wiki/YCbCr"/>
/// </summary>
public struct YCbCr : IEquatable<YCbCr>
{
/// <summary>
/// Represents a <see cref="YCbCr"/> that has Y, Cb, and Cr values set to zero.
/// </summary>
public static readonly YCbCr Empty = default(YCbCr);
/// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
/// </summary>
/// <param name="y">The y luminance component.</param>
/// <param name="cb">The cb chroma component.</param>
/// <param name="cr">The cr chroma component.</param>
public YCbCr(byte y, byte cb, byte cr)
: this()
{
this.Y = y;
this.Cb = cb;
this.Cr = cr;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public byte Y { get; }
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public byte Cb { get; }
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public byte Cr { get; }
/// <summary>
/// Gets a value indicating whether this <see cref="YCbCr"/> is empty.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsEmpty => this.Equals(Empty);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Color"/> to a
/// <see cref="YCbCr"/>.
/// </summary>
/// <param name="color">
/// The instance of <see cref="Color"/> to convert.
/// </param>
/// <returns>
/// An instance of <see cref="YCbCr"/>.
/// </returns>
public static implicit operator YCbCr(Color color)
{
byte r = color.R;
byte g = color.G;
byte b = color.B;
byte y = (byte)((0.299F * r) + (0.587F * g) + (0.114F * b));
byte cb = (byte)(128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)));
byte cr = (byte)(128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)));
return new YCbCr(y, cb, cr);
}
/// <summary>
/// Compares two <see cref="YCbCr"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="YCbCr"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(YCbCr left, YCbCr right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="YCbCr"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="YCbCr"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="YCbCr"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(YCbCr left, YCbCr right)
{
return !left.Equals(right);
}
/// <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;
}
}
/// <inheritdoc/>
public override string ToString()
{
if (this.IsEmpty)
{
return "YCbCr [ Empty ]";
}
return $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is YCbCr)
{
return this.Equals((YCbCr)obj);
}
return false;
}
/// <inheritdoc/>
public bool Equals(YCbCr other)
{
return this.Y == other.Y && this.Cb == other.Cb && this.Cr == other.Cr;
}
}
}

33
src/ImageSharp46/Colors/ComponentOrder.cs

@ -1,33 +0,0 @@
// <copyright file="ComponentOrder.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Enumerates the various component orders.
/// </summary>
public enum ComponentOrder
{
/// <summary>
/// Z-> Y-> X order. Equivalent to B-> G-> R in <see cref="Color"/>
/// </summary>
ZYX,
/// <summary>
/// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in <see cref="Color"/>
/// </summary>
ZYXW,
/// <summary>
/// X-> Y-> Z order. Equivalent to R-> G-> B in <see cref="Color"/>
/// </summary>
XYZ,
/// <summary>
/// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in <see cref="Color"/>
/// </summary>
XYZW,
}
}

31
src/ImageSharp46/Colors/PackedPixel/IPackedBytes.cs

@ -1,31 +0,0 @@
// <copyright file="IPackedBytes.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// An interface that converts packed vector types to and from <see cref="T:byte[]"/> values,
/// allowing multiple encodings to be manipulated in a generic manner.
/// </summary>
public interface IPackedBytes
{
/// <summary>
/// Gets the packed representation from the gives bytes.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
void PackFromBytes(byte x, byte y, byte z, byte w);
/// <summary>
/// Sets the packed representation into the gives bytes.
/// </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);
}
}

16
src/ImageSharp46/Colors/PackedPixel/IPackedPixel.cs

@ -1,16 +0,0 @@
// <copyright file="IPackedPixel.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// An interface that represents a packed pixel type.
/// </summary>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IPackedPixel<TPacked> : IPackedVector<TPacked>, IPackedBytes
where TPacked : struct
{
}
}

42
src/ImageSharp46/Colors/PackedPixel/IPackedVector.cs

@ -1,42 +0,0 @@
// <copyright file="IPackedVector.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Numerics;
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values,
/// allowing multiple encodings to be manipulated in a generic manner.
/// </summary>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IPackedVector<TPacked> : IPackedVector
where TPacked : struct
{
/// <summary>
/// Gets or sets the packed representation of the value.
/// </summary>
TPacked PackedValue { get; set; }
}
/// <summary>
/// An interface that converts packed vector types to and from <see cref="Vector4"/> values.
/// </summary>
public interface IPackedVector
{
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
void PackFromVector4(Vector4 vector);
/// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4();
}
}

33
src/ImageSharp46/Colors/RgbaComponent.cs

@ -1,33 +0,0 @@
// <copyright file="RgbaComponent.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
/// <summary>
/// Enumerates the RGBA (red, green, blue, alpha) color components.
/// </summary>
public enum RgbaComponent
{
/// <summary>
/// The red component.
/// </summary>
R = 0,
/// <summary>
/// The green component.
/// </summary>
G = 1,
/// <summary>
/// The blue component.
/// </summary>
B = 2,
/// <summary>
/// The alpha component.
/// </summary>
A = 3
}
}

45
src/ImageSharp46/Common/Exceptions/ImageFormatException.cs

@ -1,45 +0,0 @@
// <copyright file="ImageFormatException.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
/// <summary>
/// The exception that is thrown when the library tries to load
/// an image, which has an invalid format.
/// </summary>
public class ImageFormatException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class.
/// </summary>
public ImageFormatException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class with the name of the
/// parameter that causes this exception.
/// </summary>
/// <param name="errorMessage">The error message that explains the reason for this exception.</param>
public ImageFormatException(string errorMessage)
: base(errorMessage)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatException"/> class with a specified
/// error message and the exception that is the cause of this exception.
/// </summary>
/// <param name="errorMessage">The error message that explains the reason for this exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic)
/// if no inner exception is specified.</param>
public ImageFormatException(string errorMessage, Exception innerException)
: base(errorMessage, innerException)
{
}
}
}

44
src/ImageSharp46/Common/Exceptions/ImageProcessingException.cs

@ -1,44 +0,0 @@
// <copyright file="ImageProcessingException.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
/// <summary>
/// The exception that is thrown when an error occurs when applying a process to an image.
/// </summary>
public class ImageProcessingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class.
/// </summary>
public ImageProcessingException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class with the name of the
/// parameter that causes this exception.
/// </summary>
/// <param name="errorMessage">The error message that explains the reason for this exception.</param>
public ImageProcessingException(string errorMessage)
: base(errorMessage)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessingException"/> class with a specified
/// error message and the exception that is the cause of this exception.
/// </summary>
/// <param name="errorMessage">The error message that explains the reason for this exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic)
/// if no inner exception is specified.</param>
public ImageProcessingException(string errorMessage, Exception innerException)
: base(errorMessage, innerException)
{
}
}
}

91
src/ImageSharp46/Common/Extensions/ByteExtensions.cs

@ -1,91 +0,0 @@
// <copyright file="ByteExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
/// <summary>
/// Extension methods for the <see cref="byte"/> struct.
/// </summary>
internal static class ByteExtensions
{
/// <summary>
/// Converts a byte array to a new array where each value in the original array is represented
/// by a the specified number of bits.
/// </summary>
/// <param name="source">The bytes to convert from. Cannot be null.</param>
/// <param name="bits">The number of bits per value.</param>
/// <returns>The resulting <see cref="T:byte[]"/> array. Is never null.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
public static byte[] ToArrayByBitsLength(this byte[] source, int bits)
{
Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThan(bits, 0, "bits");
byte[] result;
if (bits < 8)
{
result = new byte[source.Length * 8 / bits];
int mask = 0xFF >> (8 - bits);
int resultOffset = 0;
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < source.Length; i++)
{
byte b = source[i];
for (int shift = 0; shift < 8; shift += bits)
{
int colorIndex = (b >> (8 - bits - shift)) & mask;
result[resultOffset] = (byte)colorIndex;
resultOffset++;
}
}
}
else
{
result = source;
}
return result;
}
/// <summary>
/// Optimized <see cref="T:byte[]"/> reversal algorithm.
/// </summary>
/// <param name="source">The byte array.</param>
public static void ReverseBytes(this byte[] source)
{
ReverseBytes(source, 0, source.Length);
}
/// <summary>
/// Optimized <see cref="T:byte[]"/> reversal algorithm.
/// </summary>
/// <param name="source">The byte array.</param>
/// <param name="index">The index.</param>
/// <param name="length">The length.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
public static void ReverseBytes(this byte[] source, int index, int length)
{
Guard.NotNull(source, nameof(source));
int i = index;
int j = index + length - 1;
while (i < j)
{
byte temp = source[i];
source[i] = source[j];
source[j] = temp;
i++;
j--;
}
}
}
}

173
src/ImageSharp46/Common/Extensions/ComparableExtensions.cs

@ -1,173 +0,0 @@
// <copyright file="ComparableExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Runtime.CompilerServices;
namespace ImageSharp
{
using System;
/// <summary>
/// Extension methods for classes that implement <see cref="IComparable{T}"/>.
/// </summary>
internal static class ComparableExtensions
{
/// <summary>
/// Restricts a <see cref="byte"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="byte"/> representing the clamped value.
/// </returns>
public static byte Clamp(this byte value, byte min, byte max)
{
// Order is important here as someone might set min to higher than max.
if (value > max)
{
return max;
}
if (value < min)
{
return min;
}
return value;
}
/// <summary>
/// Restricts a <see cref="uint"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="int"/> representing the clamped value.
/// </returns>
public static uint Clamp(this uint value, uint min, uint max)
{
if (value > max)
{
return max;
}
if (value < min)
{
return min;
}
return value;
}
/// <summary>
/// Restricts a <see cref="int"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="int"/> representing the clamped value.
/// </returns>
public static int Clamp(this int value, int min, int max)
{
if (value > max)
{
return max;
}
if (value < min)
{
return min;
}
return value;
}
/// <summary>
/// Restricts a <see cref="float"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="float"/> representing the clamped value.
/// </returns>
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Clamp(this float value, float min, float max)
{
if (value > max)
{
return max;
}
if (value < min)
{
return min;
}
return value;
}
/// <summary>
/// Restricts a <see cref="double"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="double"/> representing the clamped value.
/// </returns>
public static double Clamp(this double value, double min, double max)
{
if (value > max)
{
return max;
}
if (value < min)
{
return min;
}
return value;
}
/// <summary>
/// Converts an <see cref="int"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="int"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this int value)
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Converts an <see cref="float"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="float"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this float value)
{
return (byte)value.Clamp(0, 255);
}
/// <summary>
/// Converts an <see cref="double"/> to a <see cref="byte"/> first restricting the value between the
/// minimum and maximum allowable ranges.
/// </summary>
/// <param name="value">The <see cref="double"/> this method extends.</param>
/// <returns>The <see cref="byte"/></returns>
public static byte ToByte(this double value)
{
return (byte)value.Clamp(0, 255);
}
}
}

88
src/ImageSharp46/Common/Extensions/EnumerableExtensions.cs

@ -1,88 +0,0 @@
// <copyright file="EnumerableExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Collections.Generic;
/// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.IEnumerable"/> interface.
/// </summary>
public static class EnumerableExtensions
{
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toExclusive">
/// The end index, exclusive.
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
public static IEnumerable<int> SteppedRange(int fromInclusive, int toExclusive, int step)
{
// Borrowed from Enumerable.Range
long num = (fromInclusive + toExclusive) - 1L;
if ((toExclusive < 0) || (num > 0x7fffffffL))
{
throw new ArgumentOutOfRangeException(nameof(toExclusive));
}
return RangeIterator(fromInclusive, i => i < toExclusive, step);
}
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="bool"/> calculating the end index
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
public static IEnumerable<int> SteppedRange(int fromInclusive, Func<int, bool> toDelegate, int step)
{
return RangeIterator(fromInclusive, toDelegate, step);
}
/// <summary>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="bool"/> calculating the end index
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns>
private static IEnumerable<int> RangeIterator(int fromInclusive, Func<int, bool> toDelegate, int step)
{
int i = fromInclusive;
while (toDelegate(i))
{
yield return i;
i += step;
}
}
}
}

30
src/ImageSharp46/Common/Extensions/StreamExtensions.cs

@ -1,30 +0,0 @@
// <copyright file="StreamExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.IO;
internal static class StreamExtensions
{
public static void Skip(this Stream stream, int count)
{
if (count < 1)
{
return;
}
if (stream.CanSeek)
{
stream.Position += count;
}
else
{
byte[] foo = new byte[count];
stream.Read(foo, 0, count);
}
}
}
}

83
src/ImageSharp46/Common/Extensions/Vector4Extensions.cs

@ -1,83 +0,0 @@
// <copyright file="Vector4Extensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
/// <summary>
/// Extension methods for the <see cref="Vector4"/> struct.
/// </summary>
public static class Vector4Extensions
{
/// <summary>
/// Compresses a linear color signal to its sRGB equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="linear">The <see cref="Vector4"/> whose signal to compress.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static Vector4 Compress(this Vector4 linear)
{
// TODO: Is there a faster way to do this?
return new Vector4(Compress(linear.X), Compress(linear.Y), Compress(linear.Z), linear.W);
}
/// <summary>
/// Expands an sRGB color signal to its linear equivalent.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="gamma">The <see cref="Color"/> whose signal to expand.</param>
/// <returns>The <see cref="Vector4"/>.</returns>
public static Vector4 Expand(this Vector4 gamma)
{
// TODO: Is there a faster way to do this?
return new Vector4(Expand(gamma.X), Expand(gamma.Y), Expand(gamma.Z), gamma.W);
}
/// <summary>
/// Gets the compressed sRGB value from an linear signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to compress.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Compress(float signal)
{
if (signal <= 0.0031308F)
{
return signal * 12.92F;
}
return (1.055F * (float)Math.Pow(signal, 0.41666666F)) - 0.055F;
}
/// <summary>
/// Gets the expanded linear value from an sRGB signal.
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
/// </summary>
/// <param name="signal">The signal value to expand.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float Expand(float signal)
{
if (signal <= 0.04045F)
{
return signal / 12.92F;
}
return (float)Math.Pow((signal + 0.055F) / 1.055F, 2.4F);
}
}
}

205
src/ImageSharp46/Common/Helpers/Guard.cs

@ -1,205 +0,0 @@
// <copyright file="Guard.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Diagnostics;
/// <summary>
/// Provides methods to protect against invalid parameters.
/// </summary>
[DebuggerStepThrough]
internal static class Guard
{
/// <summary>
/// Verifies, that the method parameter with specified object value is not null
/// and throws an exception if it is found to be so.
/// </summary>
/// <param name="target">The target object, which cannot be null.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <param name="message">The error message, if any to add to the exception.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception>
public static void NotNull(object target, string parameterName, string message = "")
{
if (target == null)
{
if (!string.IsNullOrWhiteSpace(message))
{
throw new ArgumentNullException(parameterName, message);
}
throw new ArgumentNullException(parameterName);
}
}
/// <summary>
/// Verifies, that the string method parameter with specified object value and message
/// is not null, not empty and does not contain only blanks and throws an exception
/// if the object is null.
/// </summary>
/// <param name="target">The target string, which should be checked against being null or empty.</param>
/// <param name="parameterName">Name of the parameter.</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="target"/> is empty or contains only blanks.</exception>
public static void NotNullOrEmpty(string target, string parameterName)
{
if (target == null)
{
throw new ArgumentNullException(parameterName);
}
if (string.IsNullOrWhiteSpace(target))
{
throw new ArgumentException("Value cannot be null or empty and cannot contain only blanks.", parameterName);
}
}
/// <summary>
/// Verifies that the specified value is less than a maximum value
/// and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="max">The maximum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is greater than the maximum value.
/// </exception>
public static void MustBeLessThan<TValue>(TValue value, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(max) >= 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}.");
}
}
/// <summary>
/// Verifies that the specified value is less than or equal to a maximum value
/// and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="max">The maximum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is greater than the maximum value.
/// </exception>
public static void MustBeLessThanOrEqualTo<TValue>(TValue value, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(max) > 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}.");
}
}
/// <summary>
/// Verifies that the specified value is greater than a minimum value
/// and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="min">The minimum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value.
/// </exception>
public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(min) <= 0)
{
throw new ArgumentOutOfRangeException(
parameterName,
$"Value must be greater than {min}.");
}
}
/// <summary>
/// Verifies that the specified value is greater than or equal to a minimum value
/// and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="min">The minimum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value.
/// </exception>
public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(min) < 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}.");
}
}
/// <summary>
/// Verifies that the specified value is greater than or equal to a minimum value and less than
/// or equal to a maximum value and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
/// </exception>
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min} and less than or equal to {max}.");
}
}
/// <summary>
/// Verifies, that the method parameter with specified target value is true
/// and throws an exception if it is found to be so.
/// </summary>
/// <param name="target">
/// The target value, which cannot be false.
/// </param>
/// <param name="parameterName">
/// The name of the parameter that is to be checked.
/// </param>
/// <param name="message">
/// The error message, if any to add to the exception.
/// </param>
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is false
/// </exception>
public static void IsTrue(bool target, string parameterName, string message)
{
if (!target)
{
throw new ArgumentException(message, parameterName);
}
}
/// <summary>
/// Verifies, that the method parameter with specified target value is false
/// and throws an exception if it is found to be so.
/// </summary>
/// <param name="target">The target value, which cannot be true.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <param name="message">The error message, if any to add to the exception.</param>
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is true
/// </exception>
public static void IsFalse(bool target, string parameterName, string message)
{
if (target)
{
throw new ArgumentException(message, parameterName);
}
}
}
}

293
src/ImageSharp46/Common/Helpers/ImageMaths.cs

@ -1,293 +0,0 @@
// <copyright file="ImageMaths.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Linq;
using System.Numerics;
/// <summary>
/// Provides common mathematical methods.
/// </summary>
internal static class ImageMaths
{
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
/// </summary>
/// <param name="colors">The number of colors.</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
public static int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
}
/// <summary>
/// Implementation of 1D Gaussian G(x) function
/// </summary>
/// <param name="x">The x provided to G(x).</param>
/// <param name="sigma">The spread of the blur.</param>
/// <returns>The Gaussian G(x)</returns>
public static float Gaussian(float x, float sigma)
{
const float Numerator = 1.0f;
float denominator = (float)(Math.Sqrt(2 * Math.PI) * sigma);
float exponentNumerator = -x * x;
float exponentDenominator = (float)(2 * Math.Pow(sigma, 2));
float left = Numerator / denominator;
float right = (float)Math.Exp(exponentNumerator / exponentDenominator);
return left * right;
}
/// <summary>
/// Returns the result of a B-C filter against the given value.
/// <see href="http://www.imagemagick.org/Usage/filter/#cubic_bc"/>
/// </summary>
/// <param name="x">The value to process.</param>
/// <param name="b">The B-Spline curve variable.</param>
/// <param name="c">The Cardinal curve variable.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
public static float GetBcValue(float x, float b, float c)
{
float temp;
if (x < 0F)
{
x = -x;
}
temp = x * x;
if (x < 1F)
{
x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b));
return x / 6F;
}
if (x < 2F)
{
x = ((-b - (6 * c)) * (x * temp)) + (((6 * b) + (30 * c)) * temp) + (((-12 * b) - (48 * c)) * x) + ((8 * b) + (24 * c));
return x / 6F;
}
return 0F;
}
/// <summary>
/// Gets the result of a sine cardinal function for the given value.
/// </summary>
/// <param name="x">The value to calculate the result for.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
public static float SinC(float x)
{
const float Epsilon = .00001F;
if (Math.Abs(x) > Epsilon)
{
x *= (float)Math.PI;
return Clean((float)Math.Sin(x) / x);
}
return 1.0f;
}
/// <summary>
/// Returns the given degrees converted to radians.
/// </summary>
/// <param name="degrees">The angle in degrees.</param>
/// <returns>
/// The <see cref="float"/> representing the degree as radians.
/// </returns>
public static float DegreesToRadians(float degrees)
{
return degrees * (float)(Math.PI / 180);
}
/// <summary>
/// Gets the bounding <see cref="Rectangle"/> from the given points.
/// </summary>
/// <param name="topLeft">
/// The <see cref="Point"/> designating the top left position.
/// </param>
/// <param name="bottomRight">
/// The <see cref="Point"/> designating the bottom right position.
/// </param>
/// <returns>
/// The bounding <see cref="Rectangle"/>.
/// </returns>
public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight)
{
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
/// <summary>
/// Gets the bounding <see cref="Rectangle"/> from the given matrix.
/// </summary>
/// <param name="rectangle">The source rectangle.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix)
{
Vector2 leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix);
Vector2 rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix);
Vector2 leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix);
Vector2 rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom };
float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min();
float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min();
return new Rectangle(0, 0, (int)extentX, (int)extentY);
}
/// <summary>
/// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="bitmap">The <see cref="Image"/> to search within.</param>
/// <param name="componentValue">The color component value to remove.</param>
/// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param>
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
public static Rectangle GetFilteredBoundingRectangle<TColor, TPacked>(ImageBase<TColor, TPacked> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
const float Epsilon = .00001f;
int width = bitmap.Width;
int height = bitmap.Height;
Point topLeft = default(Point);
Point bottomRight = default(Point);
Func<PixelAccessor<TColor, TPacked>, int, int, float, bool> delegateFunc;
// Determine which channel to check against
switch (channel)
{
case RgbaComponent.R:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().X - b) > Epsilon;
break;
case RgbaComponent.G:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Y - b) > Epsilon;
break;
case RgbaComponent.B:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().Z - b) > Epsilon;
break;
default:
delegateFunc = (pixels, x, y, b) => Math.Abs(pixels[x, y].ToVector4().W - b) > Epsilon;
break;
}
Func<PixelAccessor<TColor, TPacked>, int> getMinY = pixels =>
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (delegateFunc(pixels, x, y, componentValue))
{
return y;
}
}
}
return 0;
};
Func<PixelAccessor<TColor, TPacked>, int> getMaxY = pixels =>
{
for (int y = height - 1; y > -1; y--)
{
for (int x = 0; x < width; x++)
{
if (delegateFunc(pixels, x, y, componentValue))
{
return y;
}
}
}
return height;
};
Func<PixelAccessor<TColor, TPacked>, int> getMinX = pixels =>
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (delegateFunc(pixels, x, y, componentValue))
{
return x;
}
}
}
return 0;
};
Func<PixelAccessor<TColor, TPacked>, int> getMaxX = pixels =>
{
for (int x = width - 1; x > -1; x--)
{
for (int y = 0; y < height; y++)
{
if (delegateFunc(pixels, x, y, componentValue))
{
return x;
}
}
}
return height;
};
using (PixelAccessor<TColor, TPacked> bitmapPixels = bitmap.Lock())
{
topLeft.Y = getMinY(bitmapPixels);
topLeft.X = getMinX(bitmapPixels);
bottomRight.Y = (getMaxY(bitmapPixels) + 1).Clamp(0, height);
bottomRight.X = (getMaxX(bitmapPixels) + 1).Clamp(0, width);
}
return GetBoundingRectangle(topLeft, bottomRight);
}
/// <summary>
/// Ensures that any passed double is correctly rounded to zero
/// </summary>
/// <param name="x">The value to clean.</param>
/// <returns>
/// The <see cref="float"/>
/// </returns>.
private static float Clean(float x)
{
const float Epsilon = .00001F;
if (Math.Abs(x) < Epsilon)
{
return 0F;
}
return x;
}
}
}

48
src/ImageSharp46/Filters/Alpha.cs

@ -1,48 +0,0 @@
// <copyright file="Alpha.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Alters the alpha component of the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>
public static Image<TColor, TPacked> Alpha<TColor, TPacked>(this Image<TColor, TPacked> source, int percent)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
return Alpha(source, percent, source.Bounds);
}
/// <summary>
/// Alters the alpha component of the image.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image<TColor, TPacked> Alpha<TColor, TPacked>(this Image<TColor, TPacked> source, int percent, Rectangle rectangle)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
return source.Process(rectangle, new AlphaProcessor<TColor, TPacked>(percent));
}
}
}

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

Loading…
Cancel
Save