Browse Source

Merge branch 'master' into master

af/merge-core
James Jackson-South 7 years ago
committed by GitHub
parent
commit
a05ccbfddf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  2. 4
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  3. 2
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  4. 6
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  5. 14
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  6. 2
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  7. 2
      src/ImageSharp/Common/Helpers/InliningOptions.cs
  8. 2
      src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
  9. 14
      src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
  10. 4
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  11. 2
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  12. 2
      src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
  13. 31
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  14. 9
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  15. 4
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  16. 4
      src/ImageSharp/Formats/IImageFormat.cs
  17. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  18. 181
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  19. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  20. 6
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  21. 79
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  22. 21
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  23. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  24. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  25. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  26. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  27. 11
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs
  28. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  29. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  30. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  31. 18
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
  32. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  33. 66
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  34. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs
  35. 2
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  36. 18
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  37. 2
      src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
  38. 2
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  39. 2
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  40. 2
      src/ImageSharp/IImageInfo.cs
  41. 24
      src/ImageSharp/Image.Decode.cs
  42. 24
      src/ImageSharp/Image.WrapMemory.cs
  43. 4
      src/ImageSharp/ImageExtensions.cs
  44. 2
      src/ImageSharp/Image{TPixel}.cs
  45. 23
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  46. 8
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs
  47. 10
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs
  48. 10
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs
  49. 4
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs
  50. 42
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs
  51. 10
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs
  52. 28
      src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs
  53. 2
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  54. 409
      src/ImageSharp/PixelFormats/ColorConstants.cs
  55. 29
      src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
  56. 2
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  57. 2
      src/ImageSharp/PixelFormats/Utils/PixelConverter.cs
  58. 2
      src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs
  59. 13
      src/ImageSharp/Processing/KnownQuantizers.cs
  60. 31
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
  61. 4
      src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs
  62. 16
      src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs
  63. 13
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs
  64. 19
      src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs
  65. 52
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs
  66. 110
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
  67. 21
      src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs
  68. 47
      src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs
  69. 48
      src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs
  70. 13
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs
  71. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs
  72. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs
  73. 4
      tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs
  74. 133
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs
  75. 404
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
  76. 6
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs
  77. 53
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs
  78. 7
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs
  79. 59
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs
  80. 35
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs
  81. 48
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs
  82. 119
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs
  83. 86
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs
  84. 96
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs
  85. 107
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs
  86. 32
      tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs
  87. 74
      tests/ImageSharp.Benchmarks/Samplers/Resize.cs
  88. 2
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
  89. 5
      tests/ImageSharp.Sandbox46/Program.cs
  90. 21
      tests/ImageSharp.Tests/Common/SimdUtilsTests.cs
  91. 35
      tests/ImageSharp.Tests/ComplexIntegrationTests.cs
  92. 3
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  93. 33
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  94. 4
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  95. 22
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs
  96. 42
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  97. 7
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs
  98. 3
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
  99. 25
      tests/ImageSharp.Tests/Image/ImageTests.cs
  100. 58
      tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs

6
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -82,11 +82,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
// we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
// region to alline with the pixel grid.
// region to align with the pixel grid.
float offset = 0.5f;
if (this.Options.Antialias)
{
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
{
// nothing on this line skip
// nothing on this line, skip
continue;
}

4
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
/// <param name="font">The font we want to render with</param>
/// <param name="brush">The brush to source pixel colors from.</param>
/// <param name="pen">The pen to outline text with.</param>
/// <param name="location">The location on the image to start drawign the text from.</param>
/// <param name="location">The location on the image to start drawing the text from.</param>
public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush<TPixel> brush, IPen<TPixel> pen, PointF location)
{
Guard.NotNull(text, nameof(text));
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
base.BeforeImageApply(source, sourceRectangle);
// do everythign at the image level as we are deligating the processing down to other processors
// do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{
ApplyKerning = this.Options.ApplyKerning,

2
src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs

@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing
{
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x);
// constrain the spans to eachother
// constrain the spans to each other
if (destinationRow.Length > scanline.Length)
{
destinationRow = destinationRow.Slice(0, scanline.Length);

6
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The span retuned from Pixel source</returns>
/// <returns>The span returned from Pixel source</returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetSpan();
@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>
/// The span retuned from Pixel source
/// The span returned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>
/// The span retuned from Pixel source
/// The span returned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>

14
src/ImageSharp/Common/Helpers/DebugGuard.cs

@ -163,6 +163,20 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Verifies whether a specific condition is met, throwing an exception if it's false.
/// </summary>
/// <param name="target">The condition</param>
/// <param name="message">The error message</param>
[Conditional("DEBUG")]
public static void IsTrue(bool target, string message)
{
if (!target)
{
throw new InvalidOperationException(message);
}
}
/// <summary>
/// Verifies, that the method parameter with specified target value is false
/// and throws an exception if it is found to be so.

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

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Scales a value from an 8 bit <see cref="byte"/> to it's 16 bit <see cref="ushort"/> equivalent.
/// </summary>
/// <param name="component">The 8 bit compoonent value.</param>
/// <param name="component">The 8 bit component value.</param>
/// <returns>The <see cref="ushort"/></returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);

2
src/ImageSharp/Common/Helpers/InliningOptions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Uncomment this for verbose profiler results:
// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
// #define PROFILING
using System.Runtime.CompilerServices;

2
src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs

@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp
var bVec = new Vector<float>(256.0f / 255.0f);
var magicFloat = new Vector<float>(32768.0f);
var magicInt = new Vector<uint>(1191182336); // reinterpreded value of 32768.0f
var magicInt = new Vector<uint>(1191182336); // reinterpreted value of 32768.0f
var mask = new Vector<uint>(255);
ref Octet.OfByte sourceBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(source));

14
src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs

@ -25,6 +25,20 @@ namespace SixLabors.ImageSharp
false;
#endif
/// <summary>
/// Widen and convert a vector of <see cref="short"/> values into 2 vectors of <see cref="float"/>-s.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ConvertToSingle(
Vector<short> source,
out Vector<float> dest1,
out Vector<float> dest2)
{
Vector.Widen(source, out Vector<int> i1, out Vector<int> i2);
dest1 = Vector.ConvertToSingle(i1);
dest2 = Vector.ConvertToSingle(i2);
}
/// <summary>
/// <see cref="BulkConvertByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
/// </summary>

4
src/ImageSharp/Common/Helpers/SimdUtils.cs

@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisable by {shouldBeDivisibleBy}!");
$"length should be divisible by {shouldBeDivisibleBy}!");
}
[Conditional("DEBUG")]
@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp
DebugGuard.IsTrue(
ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0,
nameof(source),
$"length should be divisable by {shouldBeDivisibleBy}!");
$"length should be divisible by {shouldBeDivisibleBy}!");
}
}
}

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

@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 data.
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// Compressed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>

2
src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs

@ -8,6 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
internal interface IBmpDecoderOptions
{
// added this for consistancy so we can add stuff as required, no options currently availible
// added this for consistency so we can add stuff as required, no options currently available
}
}

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

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private GifGraphicControlExtension graphicsControlExtension;
/// <summary>
/// The image desciptor.
/// The image descriptor.
/// </summary>
private GifImageDescriptor imageDescriptor;
@ -142,8 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
this.SkipBlock(); // Not supported by any known decoder.
break;
}
}
@ -190,9 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
switch (stream.ReadByte())
{
case GifConstants.GraphicControlLabel:
// Skip graphic control extension block
this.Skip(0);
this.SkipBlock(); // Skip graphic control extension block
break;
case GifConstants.CommentLabel:
this.ReadComments();
@ -201,8 +198,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadApplicationExtension();
break;
case GifConstants.PlainTextLabel:
int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
this.SkipBlock(); // Not supported by any known decoder.
break;
}
}
@ -288,24 +284,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Could be XMP or something else not supported yet.
// Back up and skip.
this.stream.Position -= appLength + 1;
this.Skip(appLength);
this.SkipBlock(appLength);
return;
}
this.Skip(appLength); // Not supported by any known decoder.
this.SkipBlock(appLength); // Not supported by any known decoder.
}
/// <summary>
/// Skips the designated number of bytes in the stream.
/// Skips over a block or reads its terminator.
/// <param name="blockSize">The length of the block to skip.</param>
/// </summary>
/// <param name="length">The number of bytes to skip.</param>
private void Skip(int length)
private void SkipBlock(int blockSize = 0)
{
this.stream.Skip(length);
if (blockSize > 0)
{
this.stream.Skip(blockSize);
}
int flag;
while ((flag = this.stream.ReadByte()) != 0)
while ((flag = this.stream.ReadByte()) > 0)
{
this.stream.Skip(flag);
}
@ -370,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
// Skip any remaining blocks
this.Skip(0);
this.SkipBlock();
}
finally
{

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

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
internal sealed class GifEncoderCore
{
/// <summary>
/// Used for allocating memory during procesing operations.
/// Used for allocating memory during processing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser);
var palleteQuantizer = new PaletteQuantizer<TPixel>(quantized.Palette, this.quantizer.Diffuser);
for (int i = 0; i < image.Frames.Count; i++)
{
@ -158,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
using (QuantizedFrame<TPixel> paletteQuantized
= palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration(), () => quantized.Palette).QuantizeFrame(frame))
using (QuantizedFrame<TPixel> paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
@ -422,7 +421,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
// The maximium number of colors for the bit depth
// The maximum number of colors for the bit depth
int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3;
int pixelCount = image.Palette.Length;

4
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
};
/// <summary>
/// The maximium number of bits/code.
/// The maximum number of bits/code.
/// </summary>
private const int MaxBits = 12;
@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// flush the packet to disk.
/// </summary>
/// <param name="c">The character to add.</param>
/// <param name="accumulatorsRef">The reference to the storage for packat accumulators</param>
/// <param name="accumulatorsRef">The reference to the storage for packet accumulators</param>
/// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream)

4
src/ImageSharp/Formats/IImageFormat.cs

@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Formats
string Name { get; }
/// <summary>
/// Gets the default mimetype that the image foramt uses
/// Gets the default mimetype that the image format uses
/// </summary>
string DefaultMimeType { get; }
/// <summary>
/// Gets all the mimetypes that have been used by this image foramt.
/// Gets all the mimetypes that have been used by this image format.
/// </summary>
IEnumerable<string> MimeTypes { get; }

6
src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs

@ -10,7 +10,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
/// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens.
/// Represents a Jpeg block with <see cref="short"/> coefficients.
/// </summary>
// ReSharper disable once InconsistentNaming
internal unsafe struct Block8x8 : IEquatable<Block8x8>
@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Gets or sets a value in a row+coulumn of the 8x8 block
/// Gets or sets a value in a row+column of the 8x8 block
/// </summary>
/// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param>
@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Calculate the total sum of absoulute differences of elements in 'a' and 'b'.
/// Calculate the total sum of absolute differences of elements in 'a' and 'b'.
/// </summary>
public static long TotalDifference(ref Block8x8 a, ref Block8x8 b)
{

181
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -3,64 +3,41 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
/// <summary>
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical.
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
this.CopyTo(area);
this.Copy1x1Scale(area);
return;
}
else if (horizontalScale == 2 && verticalScale == 2)
if (horizontalScale == 2 && verticalScale == 2)
{
this.CopyTo2x2(area);
this.Copy2x2Scale(area);
return;
}
ref float destBase = ref area.GetReferenceToOrigin();
// TODO: Optimize: implement all the cases with loopless special code! (T4?)
for (int y = 0; y < 8; y++)
{
int yy = y * verticalScale;
int y8 = y * 8;
for (int x = 0; x < 8; x++)
{
int xx = x * horizontalScale;
float value = this[y8 + x];
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * area.Stride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref destBase, baseIdx + j) = value;
}
}
}
}
// TODO: Optimize: implement all cases with scale-specific, loopless code!
this.CopyArbitraryScale(area, horizontalScale, verticalScale);
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(in BufferArea<float> area)
public void Copy1x1Scale(in BufferArea<float> destination)
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigin());
int destStride = area.Stride * sizeof(float);
ref byte destBase = ref Unsafe.As<float, byte>(ref destination.GetReferenceToOrigin());
int destStride = destination.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
@ -80,76 +57,86 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
private void CopyTo2x2(in BufferArea<float> area)
private void Copy2x2Scale(in BufferArea<float> area)
{
ref float destBase = ref area.GetReferenceToOrigin();
int destStride = area.Stride;
this.WidenCopyImpl2x2(ref destBase, 0, destStride);
this.WidenCopyImpl2x2(ref destBase, 1, destStride);
this.WidenCopyImpl2x2(ref destBase, 2, destStride);
this.WidenCopyImpl2x2(ref destBase, 3, destStride);
this.WidenCopyImpl2x2(ref destBase, 4, destStride);
this.WidenCopyImpl2x2(ref destBase, 5, destStride);
this.WidenCopyImpl2x2(ref destBase, 6, destStride);
this.WidenCopyImpl2x2(ref destBase, 7, destStride);
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref area.GetReferenceToOrigin());
int destStride = area.Stride / 2;
this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WidenCopyImpl2x2(ref float destBase, int row, int destStride)
private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride)
{
ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
int offset = 2 * row * destStride;
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
var xyLeft = new Vector4(sLeft.X);
xyLeft.Z = sLeft.Y;
xyLeft.W = sLeft.Y;
var zwLeft = new Vector4(sLeft.Z);
zwLeft.Z = sLeft.W;
zwLeft.W = sLeft.W;
var xyRight = new Vector4(sRight.X);
xyRight.Z = sRight.Y;
xyRight.W = sRight.Y;
var zwRight = new Vector4(sRight.Z);
zwRight.Z = sRight.W;
zwRight.W = sRight.W;
dTopLeft = xyLeft;
Unsafe.Add(ref dTopLeft, 1) = zwLeft;
Unsafe.Add(ref dTopLeft, 2) = xyRight;
Unsafe.Add(ref dTopLeft, 3) = zwRight;
dBottomLeft = xyLeft;
Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
Unsafe.Add(ref dBottomLeft, 2) = xyRight;
Unsafe.Add(ref dBottomLeft, 3) = zwRight;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl(ref Vector4 s, ref float destBase)
[MethodImpl(InliningOptions.ColdPath)]
private void CopyArbitraryScale(BufferArea<float> area, int horizontalScale, int verticalScale)
{
Unsafe.Add(ref destBase, 0) = s.X;
Unsafe.Add(ref destBase, 1) = s.X;
Unsafe.Add(ref destBase, 2) = s.Y;
Unsafe.Add(ref destBase, 3) = s.Y;
Unsafe.Add(ref destBase, 4) = s.Z;
Unsafe.Add(ref destBase, 5) = s.Z;
Unsafe.Add(ref destBase, 6) = s.W;
Unsafe.Add(ref destBase, 7) = s.W;
ref float destBase = ref area.GetReferenceToOrigin();
for (int y = 0; y < 8; y++)
{
int yy = y * verticalScale;
int y8 = y * 8;
for (int x = 0; x < 8; x++)
{
int xx = x * horizontalScale;
float value = this[y8 + x];
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * area.Stride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref destBase, baseIdx + j) = value;
}
}
}
}
}
}
}

6
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Transpose the block into the destination block.
/// </summary>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
d.V0L.X = V0L.X;
@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector<float> off = new Vector<float>(128f);
@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Fill the block from 'source' doing short -> float conversion.
/// </summary>
public void LoadFrom(ref Block8x8 source)
public void LoadFromInt16Scalar(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source);

6
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Transpose the block into the destination block.
/// </summary>
/// <param name="d">The destination block</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeInto(ref Block8x8F d)
{
<#
@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
{
Vector<float> off = new Vector<float>(128f);
@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Fill the block from 'source' doing short -> float conversion.
/// </summary>
public void LoadFrom(ref Block8x8 source)
public void LoadFromInt16Scalar(ref Block8x8 source)
{
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref source);

79
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <returns>The float value at the specified index</returns>
public float this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
get
{
GuardBlockIndex(idx);
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return Unsafe.Add(ref selfRef, idx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
set
{
GuardBlockIndex(idx);
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Fill the block with defaults (zeroes)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void Clear()
{
// The cheapest way to do this in C#:
@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Load raw 32bit floating point data from source
/// </summary>
/// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(Span<float> source)
{
ref byte s = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(source));
@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source)
{
blockPtr->LoadFrom(source);
@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
/// </summary>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void CopyTo(Span<float> dest)
{
ref byte d = ref Unsafe.As<float, byte>(ref MemoryMarshal.GetReference(dest));
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
/// <param name="blockPtr">Pointer to block</param>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest)
{
float* fPtr = (float*)blockPtr;
@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public static unsafe void CopyTo(Block8x8F* blockPtr, Span<float> dest)
{
blockPtr->CopyTo(dest);
@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Copy raw 32bit floating point data to dest
/// </summary>
/// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public unsafe void CopyTo(float[] dest)
{
fixed (void* ptr = &this.V0L)
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Multiply all elements of the block.
/// </summary>
/// <param name="value">The value to multiply by</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(float value)
{
this.V0L *= value;
@ -300,7 +300,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Multiply all elements of the block by the corresponding elements of 'other'
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void MultiplyInplace(ref Block8x8F other)
{
this.V0L *= other.V0L;
@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Adds a vector to all elements of the block.
/// </summary>
/// <param name="diff">The added vector</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
public void AddToAllInplace(Vector4 diff)
{
this.V0L += diff;
@ -395,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block.
/// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block.
/// </summary>
/// <param name="destination">The destination block.</param>
/// <param name="source">The source block.</param>
@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b)
{
a.V0L = DivideRound(a.V0L, b.V0L);
@ -493,6 +493,51 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
[MethodImpl(InliningOptions.ShortMethod)]
public void LoadFrom(ref Block8x8 source)
{
#if SUPPORTS_EXTENDED_INTRINSICS
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
this.LoadFromInt16ExtendedAvx2(ref source);
return;
}
#endif
this.LoadFromInt16Scalar(ref source);
}
/// <summary>
/// Loads values from <paramref name="source"/> using extended AVX2 intrinsics.
/// </summary>
/// <param name="source">The source <see cref="Block8x8"/></param>
public void LoadFromInt16ExtendedAvx2(ref Block8x8 source)
{
DebugGuard.IsTrue(
SimdUtils.IsAvx2CompatibleArchitecture,
"LoadFromUInt16ExtendedAvx2 only works on AVX2 compatible architecture!");
ref Vector<short> sRef = ref Unsafe.As<Block8x8, Vector<short>>(ref source);
ref Vector<float> dRef = ref Unsafe.As<Block8x8F, Vector<float>>(ref this);
// Vector<ushort>.Count == 16 on AVX2
// We can process 2 block rows in a single step
SimdUtils.ExtendedIntrinsics.ConvertToSingle(sRef, out Vector<float> top, out Vector<float> bottom);
dRef = top;
Unsafe.Add(ref dRef, 1) = bottom;
SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 1), out top, out bottom);
Unsafe.Add(ref dRef, 2) = top;
Unsafe.Add(ref dRef, 3) = bottom;
SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 2), out top, out bottom);
Unsafe.Add(ref dRef, 4) = top;
Unsafe.Add(ref dRef, 5) = bottom;
SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 3), out top, out bottom);
Unsafe.Add(ref dRef, 6) = top;
Unsafe.Add(ref dRef, 7) = bottom;
}
/// <inheritdoc />
public override string ToString()
{
@ -511,7 +556,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector<float> NormalizeAndRound(Vector<float> row, Vector<float> off, Vector<float> max)
{
row += off;
@ -520,13 +565,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return row.FastRound();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
// sign(dividend) = max(min(dividend, 1), -1)
var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One);
// AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend)
// AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend)
return (dividend / divisor) + (sign * Offset);
}

21
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -3,6 +3,8 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@ -17,24 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
v.X = y;
v.Y = y;
v.Z = y;
var v = new Vector4(Unsafe.Add(ref sBase, i));
v.W = 1f;
v *= scale;
result[i] = v;
Unsafe.Add(ref dBase, i) = v;
}
}
}

4
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// SIMD convert using buffers of sizes divisible by 8.
/// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!");
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
ref Vector4Pair yBase =
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0));

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// SIMD convert using buffers of sizes divisible by 8.
/// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
internal abstract partial class JpegColorConverter
{
/// <summary>
/// The avalilable converters
/// The available converters
/// </summary>
private static readonly JpegColorConverter[] Converters =
{

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
// Figure F.15: generate decoding tables for bit-sequential decoding.
// Compute largest code + 1 for this size. preshifted as we needit later.
// Compute largest code + 1 for this size. preshifted as we need it later.
Unsafe.Add(ref maxcodeRef, k) = code << (16 - k);
code <<= 1;
}

11
src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -43,16 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// Gets the <see cref="Buffer2D{Block8x8}"/> storing the "raw" frequency-domain decoded + unzigged blocks.
/// We need to apply IDCT and dequantiazition to transform them into color-space blocks.
/// We need to apply IDCT and dequantization to transform them into color-space blocks.
/// </summary>
Buffer2D<Block8x8> SpectralBlocks { get; }
/// <summary>
/// Gets a reference to the <see cref="Block8x8"/> at the given row and column index from <see cref="SpectralBlocks"/>
/// </summary>
/// <param name="column">The column</param>
/// <param name="row">The row</param>
/// <returns>The <see cref="Block8x8"/></returns>
ref Block8x8 GetBlockReference(int column, int row);
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Size ImageSizeInPixels { get; }
/// <summary>
/// Gets the number of coponents.
/// Gets the number of components.
/// </summary>
int ComponentCount { get; }

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="minorVersion">The minor version</param>
/// <param name="densityUnits">The units for the density values</param>
/// <param name="xDensity">The horizontal pixel density</param>
/// <param name="yDensity">The veritcal pixel density</param>
/// <param name="yDensity">The vertical pixel density</param>
private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity)
{
Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity));

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// - Dequantize
/// - Applying IDCT
/// - Level shift by +128, clip to [0, 255]
/// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// </summary>
/// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param>

18
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs

@ -128,21 +128,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors);
}
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(blocksPerColumnForMcu, blocksPerLineForMcu + 1, AllocationOptions.Clean);
}
int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
int width = this.WidthInBlocks + 1;
int height = totalNumberOfBlocks / width;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref Block8x8 GetBlockReference(int column, int row)
{
int offset = ((this.WidthInBlocks + 1) * row) + column;
return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.GetSpan()), offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref short GetBlockDataReference(int column, int row)
{
ref Block8x8 blockRef = ref this.GetBlockReference(column, row);
return ref Unsafe.As<Block8x8, short>(ref blockRef);
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(width, height, AllocationOptions.Clean);
}
}
}

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private int currentComponentRowInBlocks;
/// <summary>
/// The size of the area in <see cref="ColorBuffer"/> corrsponding to one 8x8 Jpeg block
/// The size of the area in <see cref="ColorBuffer"/> corresponding to one 8x8 Jpeg block
/// </summary>
private readonly Size blockAreaSize;
@ -88,12 +90,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int yBuffer = y * this.blockAreaSize.Height;
for (int x = 0; x < this.SizeInBlocks.Width; x++)
{
int xBlock = x;
int xBuffer = x * this.blockAreaSize.Width;
Span<Block8x8> blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock);
ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow);
ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock);
for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++)
{
ref Block8x8 block = ref Unsafe.Add(ref blockRowBase, xBlock);
int xBuffer = xBlock * this.blockAreaSize.Width;
BufferArea<float> destArea = this.ColorBuffer.GetArea(
xBuffer,

66
src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs

@ -1,8 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@ -142,7 +144,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y));
private void ParseBaselineData()
@ -179,10 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int h = component.HorizontalSamplingFactor;
int v = component.VerticalSamplingFactor;
int mcuRow = mcu / mcusPerLine;
// Scan out an mcu's worth of this component; that's just determined
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
int blockRow = (mcuRow * v) + y;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int x = 0; x < h; x++)
{
if (this.eof)
@ -190,15 +196,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockBaseline(
component,
blockRow,
blockCol,
ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@ -236,6 +239,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
for (int j = 0; j < h; j++)
{
// TODO: Isn't blockRow == j actually?
int blockRow = mcu / w;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int i = 0; i < w; i++)
{
if (this.eof)
@ -243,13 +250,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int blockRow = mcu / w;
// TODO: Isn't blockCol == i actually?
int blockCol = mcu % w;
this.DecodeBlockBaseline(
component,
blockRow,
blockCol,
ref blockSpan[blockCol],
ref dcHuffmanTable,
ref acHuffmanTable,
ref fastACRef);
@ -299,6 +305,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// by the basic H and V specified for the component
for (int y = 0; y < v; y++)
{
int mcuRow = mcu / mcusPerLine;
int blockRow = (mcuRow * v) + y;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int x = 0; x < h; x++)
{
if (this.eof)
@ -306,15 +316,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int mcuRow = mcu / mcusPerLine;
int mcuCol = mcu % mcusPerLine;
int blockRow = (mcuRow * v) + y;
int blockCol = (mcuCol * h) + x;
this.DecodeBlockProgressiveDC(
component,
blockRow,
blockCol,
ref blockSpan[blockCol],
ref dcHuffmanTable);
}
}
@ -351,6 +358,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int mcu = 0;
for (int j = 0; j < h; j++)
{
// TODO: isn't blockRow == j actually?
int blockRow = mcu / w;
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(blockRow);
for (int i = 0; i < w; i++)
{
if (this.eof)
@ -358,23 +369,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
return;
}
int blockRow = mcu / w;
// TODO: isn't blockCol == i actually?
int blockCol = mcu % w;
ref Block8x8 block = ref blockSpan[blockCol];
if (this.spectralStart == 0)
{
this.DecodeBlockProgressiveDC(
component,
blockRow,
blockCol,
ref block,
ref dcHuffmanTable);
}
else
{
this.DecodeBlockProgressiveAC(
component,
blockRow,
blockCol,
ref block,
ref acHuffmanTable,
ref fastACRef);
}
@ -391,8 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockBaseline(
JpegComponent component,
int row,
int col,
ref Block8x8 block,
ref HuffmanTable dcTable,
ref HuffmanTable acTable,
ref short fastACRef)
@ -405,7 +414,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowBadHuffmanCode();
}
ref short blockDataRef = ref component.GetBlockDataReference(col, row);
ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
int diff = t != 0 ? this.ExtendReceive(t) : 0;
int dc = component.DcPredictor + diff;
@ -470,8 +479,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void DecodeBlockProgressiveDC(
JpegComponent component,
int row,
int col,
ref Block8x8 block,
ref HuffmanTable dcTable)
{
if (this.spectralEnd != 0)
@ -481,7 +489,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.CheckBits();
ref short blockDataRef = ref component.GetBlockDataReference(col, row);
ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
if (this.successiveHigh == 0)
{
@ -505,9 +513,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
private void DecodeBlockProgressiveAC(
JpegComponent component,
int row,
int col,
ref Block8x8 block,
ref HuffmanTable acTable,
ref short fastACRef)
{
@ -516,7 +522,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC.");
}
ref short blockDataRef = ref component.GetBlockDataReference(col, row);
ref short blockDataRef = ref Unsafe.As<Block8x8, short>(ref block);
if (this.successiveHigh == 0)
{
@ -749,7 +755,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
[MethodImpl(InliningOptions.ColdPath)]
private void FillBuffer()
{
// Attempt to load at least the minimum nbumber of required bits into the buffer.
// Attempt to load at least the minimum number of required bits into the buffer.
// We fail to do so only if we hit a marker or reach the end of the input stream.
do
{
@ -906,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
// If it's NOT a restart, then just bail, so we get corrupt data rather than no data.
// Reset the stream to before any bad markers to ensure we can read sucessive segments.
// Reset the stream to before any bad markers to ensure we can read successive segments.
if (this.badMarker)
{
this.stream.Position = this.markerPosition;

2
src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs

@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
/// <summary>
/// Initializes the YCbCr tables
/// </summary>
/// <returns>The intialized <see cref="RgbToYCbCrTables"/></returns>
/// <returns>The initialized <see cref="RgbToYCbCrTables"/></returns>
public static RgbToYCbCrTables Create()
{
RgbToYCbCrTables tables = default;

2
src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// FOR TESTING ONLY!
/// Gets or sets a value in a row+coulumn of the 8x8 block
/// Gets or sets a value in a row+column of the 8x8 block
/// </summary>
/// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param>

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

@ -51,12 +51,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private readonly byte[] markerBuffer = new byte[2];
/// <summary>
/// The DC HUffman tables
/// The DC Huffman tables
/// </summary>
private HuffmanTables dcHuffmanTables;
/// <summary>
/// The AC HUffman tables
/// The AC Huffman tables
/// </summary>
private HuffmanTables acHuffmanTables;
@ -913,7 +913,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="index">The table index</param>
/// <param name="codeLengths">The codelengths</param>
/// <param name="values">The values</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
=> tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
@ -921,7 +921,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Reads a <see cref="ushort"/> from the stream advancing it by two bytes
/// </summary>
/// <returns>The <see cref="ushort"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16()
{
this.InputStream.Read(this.markerBuffer, 0, 2);
@ -936,12 +936,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private Image<TPixel> PostProcessIntoImage<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var image = Image.CreateUninitialized<TPixel>(
this.configuration,
this.ImageWidth,
this.ImageHeight,
this.MetaData);
using (var postProcessor = new JpegImagePostProcessor(this.configuration, this))
{
var image = new Image<TPixel>(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
postProcessor.PostProcess(image.Frames.RootFrame);
return image;
}
return image;
}
}
}

2
src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Chunks
/// <summary>
/// Constructs the PngPhysicalChunkData from the provided metadata.
/// If the resolution units are not in meters, they are automatically convereted.
/// If the resolution units are not in meters, they are automatically converted.
/// </summary>
/// <param name="meta">The metadata.</param>
/// <returns>The constructed PngPhysicalChunkData instance.</returns>

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

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline);
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
int offset = bytesPerPixel + 1; // Add one bcause x starts at one.
int offset = bytesPerPixel + 1; // Add one because x starts at one.
int x = 1;
for (; x < offset; x++)
{

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

@ -6,7 +6,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// The optioas for decoding png images
/// The options for decoding png images
/// </summary>
internal interface IPngDecoderOptions
{

2
src/ImageSharp/IImageInfo.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Encapsulates properties that descibe basic image information including dimensions, pixel type information
/// Encapsulates properties that describe basic image information including dimensions, pixel type information
/// and additional metadata
/// </summary>
public interface IImageInfo

24
src/ImageSharp/Image.Decode.cs

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@ -15,6 +16,29 @@ namespace SixLabors.ImageSharp
/// </content>
public static partial class Image
{
/// <summary>
/// Creates an <see cref="Image{TPixel}"/> instance backed by an uninitialized memory buffer.
/// This is an optimized creation method intended to be used by decoders.
/// The image might be filled with memory garbage.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="width">The width of the image</param>
/// <param name="height">The height of the image</param>
/// <param name="metadata">The <see cref="ImageMetaData"/></param>
/// <returns>The result <see cref="Image{TPixel}"/></returns>
internal static Image<TPixel> CreateUninitialized<TPixel>(
Configuration configuration,
int width,
int height,
ImageMetaData metadata)
where TPixel : struct, IPixel<TPixel>
{
Buffer2D<TPixel> uninitializedMemoryBuffer =
configuration.MemoryAllocator.Allocate2D<TPixel>(width, height);
return new Image<TPixel>(configuration, uninitializedMemoryBuffer.MemorySource, width, height, metadata);
}
/// <summary>
/// By reading the header on the provided stream this calculates the images format.
/// </summary>

24
src/ImageSharp/Image.WrapMemory.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp
public static partial class Image
{
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
/// </summary>
@ -79,15 +79,15 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
@ -105,15 +105,15 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>

4
src/ImageSharp/ImageExtensions.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp
if (format is null)
{
var sb = new StringBuilder();
sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
sb.AppendLine($"Can't find a format that is associated with the file extension '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp
if (encoder is null)
{
var sb = new StringBuilder();
sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
sb.AppendLine($"Can't find encoder for file extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");

2
src/ImageSharp/Image{TPixel}.cs

@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp
public Image<TPixel> Clone() => this.Clone(this.configuration);
/// <summary>
/// Clones the current image with the given configueation.
/// Clones the current image with the given configuration.
/// </summary>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <returns>Returns a new <see cref="Image{TPixel}"/> with all the same pixel data as the original.</returns>

23
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
private string ConvertToString(ReadOnlySpan<byte> buffer)
{
int nullCharIndex = buffer.IndexOf((byte)0);
@ -382,13 +382,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags.Add(tag);
}
[MethodImpl(InliningOptions.ShortMethod)]
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
where TEnum : struct
{
var enumValue = (TEnum)(object)value;
if (Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Any(v => v.Equals(enumValue)))
if (EnumHelper<TEnum>.IsDefined(value))
{
return enumValue;
return Unsafe.As<int, TEnum>(ref value);
}
return defaultValue;
@ -557,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
private class EnumHelper<TEnum>
where TEnum : struct
{
private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>()
.Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
[MethodImpl(InliningOptions.ShortMethod)]
public static bool IsDefined(int value)
{
return Array.BinarySearch(Values, value) >= 0;
}
}
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs

@ -41,10 +41,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
public IccResponseCurve ReadResponseCurve(int channelCount)
{
var type = (IccCurveMeasurementEncodings)this.ReadUInt32();
uint[] measurment = new uint[channelCount];
uint[] measurement = new uint[channelCount];
for (int i = 0; i < channelCount; i++)
{
measurment[i] = this.ReadUInt32();
measurement[i] = this.ReadUInt32();
}
Vector3[] xyzValues = new Vector3[channelCount];
@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
IccResponseNumber[][] response = new IccResponseNumber[channelCount][];
for (int i = 0; i < channelCount; i++)
{
response[i] = new IccResponseNumber[measurment[i]];
for (uint j = 0; j < measurment[i]; j++)
response[i] = new IccResponseNumber[measurement[i]];
for (uint j = 0; j < measurement[i]; j++)
{
response[i][j] = this.ReadResponseNumber();
}

10
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs

@ -628,16 +628,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{
int start = this.currentIndex - 8; // 8 is the tag header size
ushort channelCount = this.ReadUInt16();
ushort measurmentCount = this.ReadUInt16();
ushort measurementCount = this.ReadUInt16();
uint[] offset = new uint[measurmentCount];
for (int i = 0; i < measurmentCount; i++)
uint[] offset = new uint[measurementCount];
for (int i = 0; i < measurementCount; i++)
{
offset[i] = this.ReadUInt32();
}
var curves = new IccResponseCurve[measurmentCount];
for (int i = 0; i < measurmentCount; i++)
var curves = new IccResponseCurve[measurementCount];
for (int i = 0; i < measurementCount; i++)
{
this.currentIndex = (int)(start + offset[i]);
curves[i] = this.ReadResponseCurve(channelCount);

10
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs

@ -39,15 +39,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary>
/// This profile provides the relevant information to perform a transformation
/// between colour encodings and the PCS. This type of profile is based on
/// modelling rather than device measurement or characterization data.
/// between color encodings and the PCS. This type of profile is based on
/// modeling rather than device measurement or characterization data.
/// ColorSpace profiles may be embedded in images.
/// </summary>
ColorSpace = 0x73706163, // spac
/// <summary>
/// This profile represents abstract transforms and does not represent any
/// device model. Colour transformations using Abstract profiles are performed
/// device model. Color transformations using Abstract profiles are performed
/// from PCS to PCS. Abstract profiles cannot be embedded in images.
/// </summary>
Abstract = 0x61627374, // abst
@ -55,8 +55,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <summary>
/// NamedColor profiles can be thought of as sibling profiles to device profiles.
/// For a given device there would be one or more device profiles to handle
/// process colour conversions and one or more named colour profiles to handle
/// named colours.
/// process color conversions and one or more named color profiles to handle
/// named colors.
/// </summary>
NamedColor = 0x6E6D636C, // nmcl
}

4
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs

@ -29,12 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
NotEmbedded = 0,
/// <summary>
/// Profile cannot be used independently of the embedded colour data
/// Profile cannot be used independently of the embedded color data
/// </summary>
NotIndependent = 1 << 1,
/// <summary>
/// Profile can be used independently of the embedded colour data
/// Profile can be used independently of the embedded color data
/// </summary>
Independent = 0,
}

42
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs

@ -19,18 +19,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
Unknown,
/// <summary>
/// A2B0 - This tag defines a colour transform from Device, Colour Encoding or PCS, to PCS, or a colour transform
/// A2B0 - This tag defines a color transform from Device, Color Encoding or PCS, to PCS, or a color transform
/// from Device 1 to Device 2, using lookup table tag element structures
/// </summary>
AToB0 = 0x41324230,
/// <summary>
/// A2B2 - This tag describes the colour transform from Device or Colour Encoding to PCS using lookup table tag element structures
/// A2B2 - This tag describes the color transform from Device or Color Encoding to PCS using lookup table tag element structures
/// </summary>
AToB1 = 0x41324231,
/// <summary>
/// A2B2 - This tag describes the colour transform from Device or Colour Encoding to PCS using lookup table tag element structures
/// A2B2 - This tag describes the color transform from Device or Color Encoding to PCS using lookup table tag element structures
/// </summary>
AToB2 = 0x41324232,
@ -46,40 +46,40 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
BlueTrc = 0x62545243,
/// <summary>
/// B2A0 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures
/// B2A0 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures
/// </summary>
BToA0 = 0x42324130,
/// <summary>
/// B2A1 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures.
/// B2A1 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures.
/// </summary>
BToA1 = 0x42324131,
/// <summary>
/// B2A2 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures.
/// B2A2 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures.
/// </summary>
BToA2 = 0x42324132,
/// <summary>
/// B2D0 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D0 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA0 tag.
/// </summary>
BToD0 = 0x42324430,
/// <summary>
/// B2D1 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D1 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA1 tag.
/// </summary>
BToD1 = 0x42324431,
/// <summary>
/// B2D2 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D2 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA2 tag.
/// </summary>
BToD2 = 0x42324432,
/// <summary>
/// B2D3 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// B2D3 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and
/// provides a means to override the BToA1 tag.
/// </summary>
BToD3 = 0x42324433,
@ -97,8 +97,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
CharTarget = 0x74617267,
/// <summary>
/// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ colour, measured using the actual illumination
/// conditions and relative to the actual adopted white, to an nCIEXYZ colour relative to the PCS adopted white
/// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ color, measured using the actual illumination
/// conditions and relative to the actual adopted white, to an nCIEXYZ color relative to the PCS adopted white
/// </summary>
ChromaticAdaptation = 0x63686164,
@ -166,33 +166,33 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
DeviceSettings = 0x64657673,
/// <summary>
/// D2B0 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B0 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB0 tag
/// </summary>
DToB0 = 0x44324230,
/// <summary>
/// D2B1 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B1 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary>
DToB1 = 0x44324230,
/// <summary>
/// D2B2 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B2 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary>
DToB2 = 0x44324230,
/// <summary>
/// D2B3 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded
/// D2B3 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded
/// input range, output range and transform, and provides a means to override the AToB1 tag
/// </summary>
DToB3 = 0x44324230,
/// <summary>
/// gamt - This tag provides a table in which PCS values are the input and a single
/// output value for each input value is the output. If the output value is 0, the PCS colour is in-gamut.
/// If the output is non-zero, the PCS colour is out-of-gamut
/// output value for each input value is the output. If the output value is 0, the PCS color is in-gamut.
/// If the output is non-zero, the PCS color is out-of-gamut
/// </summary>
Gamut = 0x67616D74,
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
GreenTrc = 0x67545243,
/// <summary>
/// lumi - This tag contains the absolute luminance of emissive devices in candelas per square metre as described by the Y channel.
/// lumi - This tag contains the absolute luminance of emissive devices in candelas per square meter as described by the Y channel.
/// </summary>
Luminance = 0x6C756d69,
@ -240,8 +240,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
NamedColor = 0x6E636f6C,
/// <summary>
/// ncl2 - This tag contains the named colour information providing a PCS and optional device representation
/// for a list of named colours.
/// ncl2 - This tag contains the named color information providing a PCS and optional device representation
/// for a list of named colors.
/// </summary>
NamedColor2 = 0x6E636C32,

10
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs

@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{
/// <summary>
/// In perceptual transforms the PCS values represent hypothetical
/// measurements of a colour reproduction on the reference reflective
/// measurements of a color reproduction on the reference reflective
/// medium. By extension, for the perceptual intent, the PCS represents
/// the appearance of that reproduction as viewed in the reference viewing
/// environment by a human observer adapted to that environment. The exact
/// colour rendering of the perceptual intent is vendor specific.
/// color rendering of the perceptual intent is vendor specific.
/// </summary>
Perceptual = 0,
@ -27,15 +27,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MediaRelativeColorimetric = 1,
/// <summary>
/// The exact colour rendering of the saturation intent is vendor
/// The exact color rendering of the saturation intent is vendor
/// specific and involves compromises such as trading off
/// preservation of hue in order to preserve the vividness of pure colours.
/// preservation of hue in order to preserve the vividness of pure colors.
/// </summary>
Saturation = 2,
/// <summary>
/// Transformations for this intent shall leave the chromatically
/// adapted nCIEXYZ tristimulus values of the in-gamut colours unchanged.
/// adapted nCIEXYZ tristimulus values of the in-gamut colors unchanged.
/// </summary>
AbsoluteColorimetric = 3,
}

28
src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// This is an optional tag which specifies the laydown order in which colorants
/// will be printed on an n-colorant device. The laydown order may be the same
/// as the channel generation order listed in the colorantTableTag or the channel
/// order of a colour encoding type such as CMYK, in which case this tag is not
/// order of a color encoding type such as CMYK, in which case this tag is not
/// needed. When this is not the case (for example, ink-towers sometimes use
/// the order KCMY), this tag may be used to specify the laydown order of the
/// colorants
@ -59,25 +59,25 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
DateTime = 0x6474696D,
/// <summary>
/// This structure represents a colour transform using tables with 16-bit
/// This structure represents a color transform using tables with 16-bit
/// precision. This type contains four processing elements: a 3 × 3 matrix
/// (which shall be the identity matrix unless the input colour space is
/// (which shall be the identity matrix unless the input color space is
/// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional
/// lookup table, and a set of one-dimensional output tables
/// </summary>
Lut16 = 0x6D667432,
/// <summary>
/// This structure represents a colour transform using tables of 8-bit
/// This structure represents a color transform using tables of 8-bit
/// precision. This type contains four processing elements: a 3 × 3 matrix
/// (which shall be the identity matrix unless the input colour space is
/// (which shall be the identity matrix unless the input color space is
/// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional
/// lookup table, and a set of one-dimensional output tables.
/// </summary>
Lut8 = 0x6D667431,
/// <summary>
/// This structure represents a colour transform. The type contains up
/// This structure represents a color transform. The type contains up
/// to five processing elements which are stored in the AToBTag tag
/// in the following order: a set of one-dimensional curves, a 3 × 3
/// matrix with offset terms, a set of one-dimensional curves, a
@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
LutAToB = 0x6D414220,
/// <summary>
/// This structure represents a colour transform. The type contains
/// This structure represents a color transform. The type contains
/// up to five processing elements which are stored in the BToATag
/// in the following order: a set of one-dimensional curves, a 3 × 3
/// matrix with offset terms, a set of one-dimensional curves, a
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MultiLocalizedUnicode = 0x6D6C7563,
/// <summary>
/// This structure represents a colour transform, containing a sequence
/// This structure represents a color transform, containing a sequence
/// of processing elements. The processing elements contained in the
/// structure are defined in the structure itself, allowing for a flexible
/// structure. Currently supported processing elements are: a set of one
@ -123,15 +123,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
MultiProcessElements = 0x6D706574,
/// <summary>
/// This type is a count value and array of structures that provide colour
/// coordinates for colour names. For each named colour, a PCS and optional
/// device representation of the colour are given. Both representations are
/// This type is a count value and array of structures that provide color
/// coordinates for color names. For each named color, a PCS and optional
/// device representation of the color are given. Both representations are
/// 16-bit values and PCS values shall be relative colorimetric. The device
/// representation corresponds to the header’s "data colour space" field.
/// representation corresponds to the header’s "data color space" field.
/// This representation should be consistent with the "number of device
/// coordinates" field in the namedColor2Type. If this field is 0, device
/// coordinates are not provided. The PCS representation corresponds to the
/// header's PCS field. The PCS representation is always provided. Colour
/// header's PCS field. The PCS representation is always provided. Color
/// names are fixed-length, 32-byte fields including null termination. In
/// order to maintain maximum portability, it is strongly recommended that
/// special characters of the 7-bit ASCII set not be used.
@ -168,7 +168,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// so that corrections can be made for variation in the device without
/// having to produce a new profile. The mechanism can be used by applications
/// to allow users with relatively inexpensive and readily available
/// instrumentation to apply corrections to individual output colour
/// instrumentation to apply corrections to individual output color
/// channels in order to achieve consistent results.
/// </summary>
ResponseCurveSet16 = 0x72637332,

2
src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// The hexadecimal representation of the combined color components arranged
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
/// </param>
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA heax string.</returns>
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA hex string.</returns>
public static TPixel FromHex(string hex)
{
Guard.NotNullOrWhiteSpace(hex, nameof(hex));

409
src/ImageSharp/PixelFormats/ColorConstants.cs

@ -11,157 +11,268 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4.
/// </summary>
public static readonly Rgba32[] WebSafeColors = GetWebSafeColors();
public static readonly Rgba32[] WebSafeColors =
{
Rgba32.AliceBlue,
Rgba32.AntiqueWhite,
Rgba32.Aqua,
Rgba32.Aquamarine,
Rgba32.Azure,
Rgba32.Beige,
Rgba32.Bisque,
Rgba32.Black,
Rgba32.BlanchedAlmond,
Rgba32.Blue,
Rgba32.BlueViolet,
Rgba32.Brown,
Rgba32.BurlyWood,
Rgba32.CadetBlue,
Rgba32.Chartreuse,
Rgba32.Chocolate,
Rgba32.Coral,
Rgba32.CornflowerBlue,
Rgba32.Cornsilk,
Rgba32.Crimson,
Rgba32.Cyan,
Rgba32.DarkBlue,
Rgba32.DarkCyan,
Rgba32.DarkGoldenrod,
Rgba32.DarkGray,
Rgba32.DarkGreen,
Rgba32.DarkKhaki,
Rgba32.DarkMagenta,
Rgba32.DarkOliveGreen,
Rgba32.DarkOrange,
Rgba32.DarkOrchid,
Rgba32.DarkRed,
Rgba32.DarkSalmon,
Rgba32.DarkSeaGreen,
Rgba32.DarkSlateBlue,
Rgba32.DarkSlateGray,
Rgba32.DarkTurquoise,
Rgba32.DarkViolet,
Rgba32.DeepPink,
Rgba32.DeepSkyBlue,
Rgba32.DimGray,
Rgba32.DodgerBlue,
Rgba32.Firebrick,
Rgba32.FloralWhite,
Rgba32.ForestGreen,
Rgba32.Fuchsia,
Rgba32.Gainsboro,
Rgba32.GhostWhite,
Rgba32.Gold,
Rgba32.Goldenrod,
Rgba32.Gray,
Rgba32.Green,
Rgba32.GreenYellow,
Rgba32.Honeydew,
Rgba32.HotPink,
Rgba32.IndianRed,
Rgba32.Indigo,
Rgba32.Ivory,
Rgba32.Khaki,
Rgba32.Lavender,
Rgba32.LavenderBlush,
Rgba32.LawnGreen,
Rgba32.LemonChiffon,
Rgba32.LightBlue,
Rgba32.LightCoral,
Rgba32.LightCyan,
Rgba32.LightGoldenrodYellow,
Rgba32.LightGray,
Rgba32.LightGreen,
Rgba32.LightPink,
Rgba32.LightSalmon,
Rgba32.LightSeaGreen,
Rgba32.LightSkyBlue,
Rgba32.LightSlateGray,
Rgba32.LightSteelBlue,
Rgba32.LightYellow,
Rgba32.Lime,
Rgba32.LimeGreen,
Rgba32.Linen,
Rgba32.Magenta,
Rgba32.Maroon,
Rgba32.MediumAquamarine,
Rgba32.MediumBlue,
Rgba32.MediumOrchid,
Rgba32.MediumPurple,
Rgba32.MediumSeaGreen,
Rgba32.MediumSlateBlue,
Rgba32.MediumSpringGreen,
Rgba32.MediumTurquoise,
Rgba32.MediumVioletRed,
Rgba32.MidnightBlue,
Rgba32.MintCream,
Rgba32.MistyRose,
Rgba32.Moccasin,
Rgba32.NavajoWhite,
Rgba32.Navy,
Rgba32.OldLace,
Rgba32.Olive,
Rgba32.OliveDrab,
Rgba32.Orange,
Rgba32.OrangeRed,
Rgba32.Orchid,
Rgba32.PaleGoldenrod,
Rgba32.PaleGreen,
Rgba32.PaleTurquoise,
Rgba32.PaleVioletRed,
Rgba32.PapayaWhip,
Rgba32.PeachPuff,
Rgba32.Peru,
Rgba32.Pink,
Rgba32.Plum,
Rgba32.PowderBlue,
Rgba32.Purple,
Rgba32.RebeccaPurple,
Rgba32.Red,
Rgba32.RosyBrown,
Rgba32.RoyalBlue,
Rgba32.SaddleBrown,
Rgba32.Salmon,
Rgba32.SandyBrown,
Rgba32.SeaGreen,
Rgba32.SeaShell,
Rgba32.Sienna,
Rgba32.Silver,
Rgba32.SkyBlue,
Rgba32.SlateBlue,
Rgba32.SlateGray,
Rgba32.Snow,
Rgba32.SpringGreen,
Rgba32.SteelBlue,
Rgba32.Tan,
Rgba32.Teal,
Rgba32.Thistle,
Rgba32.Tomato,
Rgba32.Transparent,
Rgba32.Turquoise,
Rgba32.Violet,
Rgba32.Wheat,
Rgba32.White,
Rgba32.WhiteSmoke,
Rgba32.Yellow,
Rgba32.YellowGreen
};
/// <summary>
/// Returns an array of web safe colors.
/// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
/// <returns>The <see cref="T:Color[]"/></returns>
private static Rgba32[] GetWebSafeColors()
=> new Rgba32[]
{
Rgba32.AliceBlue,
Rgba32.AntiqueWhite,
Rgba32.Aqua,
Rgba32.Aquamarine,
Rgba32.Azure,
Rgba32.Beige,
Rgba32.Bisque,
Rgba32.Black,
Rgba32.BlanchedAlmond,
Rgba32.Blue,
Rgba32.BlueViolet,
Rgba32.Brown,
Rgba32.BurlyWood,
Rgba32.CadetBlue,
Rgba32.Chartreuse,
Rgba32.Chocolate,
Rgba32.Coral,
Rgba32.CornflowerBlue,
Rgba32.Cornsilk,
Rgba32.Crimson,
Rgba32.Cyan,
Rgba32.DarkBlue,
Rgba32.DarkCyan,
Rgba32.DarkGoldenrod,
Rgba32.DarkGray,
Rgba32.DarkGreen,
Rgba32.DarkKhaki,
Rgba32.DarkMagenta,
Rgba32.DarkOliveGreen,
Rgba32.DarkOrange,
Rgba32.DarkOrchid,
Rgba32.DarkRed,
Rgba32.DarkSalmon,
Rgba32.DarkSeaGreen,
Rgba32.DarkSlateBlue,
Rgba32.DarkSlateGray,
Rgba32.DarkTurquoise,
Rgba32.DarkViolet,
Rgba32.DeepPink,
Rgba32.DeepSkyBlue,
Rgba32.DimGray,
Rgba32.DodgerBlue,
Rgba32.Firebrick,
Rgba32.FloralWhite,
Rgba32.ForestGreen,
Rgba32.Fuchsia,
Rgba32.Gainsboro,
Rgba32.GhostWhite,
Rgba32.Gold,
Rgba32.Goldenrod,
Rgba32.Gray,
Rgba32.Green,
Rgba32.GreenYellow,
Rgba32.Honeydew,
Rgba32.HotPink,
Rgba32.IndianRed,
Rgba32.Indigo,
Rgba32.Ivory,
Rgba32.Khaki,
Rgba32.Lavender,
Rgba32.LavenderBlush,
Rgba32.LawnGreen,
Rgba32.LemonChiffon,
Rgba32.LightBlue,
Rgba32.LightCoral,
Rgba32.LightCyan,
Rgba32.LightGoldenrodYellow,
Rgba32.LightGray,
Rgba32.LightGreen,
Rgba32.LightPink,
Rgba32.LightSalmon,
Rgba32.LightSeaGreen,
Rgba32.LightSkyBlue,
Rgba32.LightSlateGray,
Rgba32.LightSteelBlue,
Rgba32.LightYellow,
Rgba32.Lime,
Rgba32.LimeGreen,
Rgba32.Linen,
Rgba32.Magenta,
Rgba32.Maroon,
Rgba32.MediumAquamarine,
Rgba32.MediumBlue,
Rgba32.MediumOrchid,
Rgba32.MediumPurple,
Rgba32.MediumSeaGreen,
Rgba32.MediumSlateBlue,
Rgba32.MediumSpringGreen,
Rgba32.MediumTurquoise,
Rgba32.MediumVioletRed,
Rgba32.MidnightBlue,
Rgba32.MintCream,
Rgba32.MistyRose,
Rgba32.Moccasin,
Rgba32.NavajoWhite,
Rgba32.Navy,
Rgba32.OldLace,
Rgba32.Olive,
Rgba32.OliveDrab,
Rgba32.Orange,
Rgba32.OrangeRed,
Rgba32.Orchid,
Rgba32.PaleGoldenrod,
Rgba32.PaleGreen,
Rgba32.PaleTurquoise,
Rgba32.PaleVioletRed,
Rgba32.PapayaWhip,
Rgba32.PeachPuff,
Rgba32.Peru,
Rgba32.Pink,
Rgba32.Plum,
Rgba32.PowderBlue,
Rgba32.Purple,
Rgba32.RebeccaPurple,
Rgba32.Red,
Rgba32.RosyBrown,
Rgba32.RoyalBlue,
Rgba32.SaddleBrown,
Rgba32.Salmon,
Rgba32.SandyBrown,
Rgba32.SeaGreen,
Rgba32.SeaShell,
Rgba32.Sienna,
Rgba32.Silver,
Rgba32.SkyBlue,
Rgba32.SlateBlue,
Rgba32.SlateGray,
Rgba32.Snow,
Rgba32.SpringGreen,
Rgba32.SteelBlue,
Rgba32.Tan,
Rgba32.Teal,
Rgba32.Thistle,
Rgba32.Tomato,
Rgba32.Transparent,
Rgba32.Turquoise,
Rgba32.Violet,
Rgba32.Wheat,
Rgba32.White,
Rgba32.WhiteSmoke,
Rgba32.Yellow,
Rgba32.YellowGreen
};
public static readonly Rgba32[] WernerColors =
{
Rgba32.FromHex("#f1e9cd"),
Rgba32.FromHex("#f2e7cf"),
Rgba32.FromHex("#ece6d0"),
Rgba32.FromHex("#f2eacc"),
Rgba32.FromHex("#f3e9ca"),
Rgba32.FromHex("#f2ebcd"),
Rgba32.FromHex("#e6e1c9"),
Rgba32.FromHex("#e2ddc6"),
Rgba32.FromHex("#cbc8b7"),
Rgba32.FromHex("#bfbbb0"),
Rgba32.FromHex("#bebeb3"),
Rgba32.FromHex("#b7b5ac"),
Rgba32.FromHex("#bab191"),
Rgba32.FromHex("#9c9d9a"),
Rgba32.FromHex("#8a8d84"),
Rgba32.FromHex("#5b5c61"),
Rgba32.FromHex("#555152"),
Rgba32.FromHex("#413f44"),
Rgba32.FromHex("#454445"),
Rgba32.FromHex("#423937"),
Rgba32.FromHex("#433635"),
Rgba32.FromHex("#252024"),
Rgba32.FromHex("#241f20"),
Rgba32.FromHex("#281f3f"),
Rgba32.FromHex("#1c1949"),
Rgba32.FromHex("#4f638d"),
Rgba32.FromHex("#383867"),
Rgba32.FromHex("#5c6b8f"),
Rgba32.FromHex("#657abb"),
Rgba32.FromHex("#6f88af"),
Rgba32.FromHex("#7994b5"),
Rgba32.FromHex("#6fb5a8"),
Rgba32.FromHex("#719ba2"),
Rgba32.FromHex("#8aa1a6"),
Rgba32.FromHex("#d0d5d3"),
Rgba32.FromHex("#8590ae"),
Rgba32.FromHex("#3a2f52"),
Rgba32.FromHex("#39334a"),
Rgba32.FromHex("#6c6d94"),
Rgba32.FromHex("#584c77"),
Rgba32.FromHex("#533552"),
Rgba32.FromHex("#463759"),
Rgba32.FromHex("#bfbac0"),
Rgba32.FromHex("#77747f"),
Rgba32.FromHex("#4a475c"),
Rgba32.FromHex("#b8bfaf"),
Rgba32.FromHex("#b2b599"),
Rgba32.FromHex("#979c84"),
Rgba32.FromHex("#5d6161"),
Rgba32.FromHex("#61ac86"),
Rgba32.FromHex("#a4b6a7"),
Rgba32.FromHex("#adba98"),
Rgba32.FromHex("#93b778"),
Rgba32.FromHex("#7d8c55"),
Rgba32.FromHex("#33431e"),
Rgba32.FromHex("#7c8635"),
Rgba32.FromHex("#8e9849"),
Rgba32.FromHex("#c2c190"),
Rgba32.FromHex("#67765b"),
Rgba32.FromHex("#ab924b"),
Rgba32.FromHex("#c8c76f"),
Rgba32.FromHex("#ccc050"),
Rgba32.FromHex("#ebdd99"),
Rgba32.FromHex("#ab9649"),
Rgba32.FromHex("#dbc364"),
Rgba32.FromHex("#e6d058"),
Rgba32.FromHex("#ead665"),
Rgba32.FromHex("#d09b2c"),
Rgba32.FromHex("#a36629"),
Rgba32.FromHex("#a77d35"),
Rgba32.FromHex("#f0d696"),
Rgba32.FromHex("#d7c485"),
Rgba32.FromHex("#f1d28c"),
Rgba32.FromHex("#efcc83"),
Rgba32.FromHex("#f3daa7"),
Rgba32.FromHex("#dfa837"),
Rgba32.FromHex("#ebbc71"),
Rgba32.FromHex("#d17c3f"),
Rgba32.FromHex("#92462f"),
Rgba32.FromHex("#be7249"),
Rgba32.FromHex("#bb603c"),
Rgba32.FromHex("#c76b4a"),
Rgba32.FromHex("#a75536"),
Rgba32.FromHex("#b63e36"),
Rgba32.FromHex("#b5493a"),
Rgba32.FromHex("#cd6d57"),
Rgba32.FromHex("#711518"),
Rgba32.FromHex("#e9c49d"),
Rgba32.FromHex("#eedac3"),
Rgba32.FromHex("#eecfbf"),
Rgba32.FromHex("#ce536b"),
Rgba32.FromHex("#b74a70"),
Rgba32.FromHex("#b7757c"),
Rgba32.FromHex("#612741"),
Rgba32.FromHex("#7a4848"),
Rgba32.FromHex("#3f3033"),
Rgba32.FromHex("#8d746f"),
Rgba32.FromHex("#4d3635"),
Rgba32.FromHex("#6e3b31"),
Rgba32.FromHex("#864735"),
Rgba32.FromHex("#553d3a"),
Rgba32.FromHex("#613936"),
Rgba32.FromHex("#7a4b3a"),
Rgba32.FromHex("#946943"),
Rgba32.FromHex("#c39e6d"),
Rgba32.FromHex("#513e32"),
Rgba32.FromHex("#8b7859"),
Rgba32.FromHex("#9b856b"),
Rgba32.FromHex("#766051"),
Rgba32.FromHex("#453b32")
};
}
}

29
src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs

@ -14,9 +14,10 @@ namespace SixLabors.ImageSharp.PixelFormats
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Thread-safe backing field for <see cref="WebSafePalette"/>.
/// Thread-safe backing field for the constant palettes.
/// </summary>
private static readonly Lazy<TPixel[]> WebSafePaletteLazy = new Lazy<TPixel[]>(GetWebSafePalette, true);
private static readonly Lazy<TPixel[]> WernerPaletteLazy = new Lazy<TPixel[]>(GetWernerPalette, true);
/// <summary>
/// Represents a <see paramref="TPixel"/> matching the W3C definition that has an hex value of #F0F8FF.
@ -729,22 +730,32 @@ namespace SixLabors.ImageSharp.PixelFormats
public static readonly TPixel YellowGreen = ColorBuilder<TPixel>.FromRGBA(154, 205, 50, 255);
/// <summary>
/// Gets a <see cref="T:TPixel[]"/> matching the W3C definition of web safe colors.
/// Gets a <see cref="T:TPixel[]"/> collection of web safe, colors as defined in the CSS Color Module Level 4.
/// </summary>
public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value;
private static TPixel[] GetWebSafePalette()
/// <summary>
/// Gets a <see cref="T:TPixel[]"/> collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
public static TPixel[] WernerPalette => WernerPaletteLazy.Value;
private static TPixel[] GetWebSafePalette() => GetPalette(ColorConstants.WebSafeColors);
private static TPixel[] GetWernerPalette() => GetPalette(ColorConstants.WernerColors);
private static TPixel[] GetPalette(Rgba32[] palette)
{
Rgba32[] constants = ColorConstants.WebSafeColors;
var safe = new TPixel[constants.Length + 1];
var converted = new TPixel[palette.Length];
Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(constants.AsSpan());
Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(palette.AsSpan());
PixelOperations<TPixel>.Instance.FromRgba32Bytes(
Configuration.Default,
constantsBytes,
safe,
constants.Length);
return safe;
converted,
palette.Length);
return converted;
}
}
}

2
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
// Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the
// standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 alogrithm.
// standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm.
// One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and
// packs/unpacks the pixel without and conversion so we employ custom methods do do this.
if (typeof(TDestinationPixel) == typeof(Gray16))

2
src/ImageSharp/PixelFormats/Utils/PixelConverter.cs

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
// packedRgba = [aa bb gg rr]
// tmp1 = [aa 00 gg 00]
// tmp2 = [00 bb 00 rr]
// tmp3=ROTL(16, tmp2) = [00 rr 00 bb]
// tmp3=ROTL(16, tmp2) = [00 rr 00 bb]
// tmp1 + tmp3 = [aa rr gg bb]
uint tmp1 = packedRgba & 0xFF00FF00;
uint tmp2 = packedRgba & 0x00FF00FF;

2
src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs

@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats.Utils
Span<Rgba32> lastQuarterOfDestBuffer = MemoryMarshal.Cast<Vector4, Rgba32>(destVectors).Slice((3 * count) + 1, countWithoutLastItem);
pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer);
// 'destVectors' and 'lastQuarterOfDestBuffer' are ovelapping buffers,
// 'destVectors' and 'lastQuarterOfDestBuffer' are overlapping buffers,
// but we are always reading/writing at different positions:
SimdUtils.BulkConvertByteToNormalizedFloat(
MemoryMarshal.Cast<Rgba32, byte>(lastQuarterOfDestBuffer),

13
src/ImageSharp/Processing/KnownQuantizers.cs

@ -12,20 +12,23 @@ namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Gets the adaptive Octree quantizer. Fast with good quality.
/// The quantizer only supports a single alpha value.
/// </summary>
public static IQuantizer Octree { get; } = new OctreeQuantizer();
/// <summary>
/// Gets the Xiaolin Wu's Color Quantizer which generates high quality output.
/// The quantizer supports multiple alpha values.
/// </summary>
public static IQuantizer Wu { get; } = new WuQuantizer();
/// <summary>
/// Gets the palette based, Using the collection of web-safe colors.
/// The quantizer supports multiple alpha values.
/// Gets the palette based quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
/// </summary>
public static IQuantizer Palette { get; } = new PaletteQuantizer();
public static IQuantizer WebSafe { get; } = new WebSafePaletteQuantizer();
/// <summary>
/// Gets the palette based quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
public static IQuantizer Werner { get; } = new WernerPaletteQuantizer();
}
}

31
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs

@ -66,36 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
? new DenseMatrix<float>(size, 1)
: new DenseMatrix<float>(1, size);
float sum = 0F;
for (int i = 0; i < size; i++)
{
float x = 1;
sum += x;
if (horizontal)
{
kernel[0, i] = x;
}
else
{
kernel[i, 0] = x;
}
}
// Normalize kernel so that the sum of all weights equals 1
if (horizontal)
{
for (int i = 0; i < size; i++)
{
kernel[0, i] = kernel[0, i] / sum;
}
}
else
{
for (int i = 0; i < size; i++)
{
kernel[i, 0] = kernel[i, 0] / sum;
}
}
kernel.Fill(1.0F / size);
return kernel;
}

4
src/ImageSharp/Processing/Processors/Convolution/KirshKernels.cs → src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs

@ -6,9 +6,9 @@ using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
/// Contains the eight matrices used for Kirsh edge detection
/// Contains the eight matrices used for Kirsch edge detection
/// </summary>
internal static class KirshKernels
internal static class KirschKernels
{
/// <summary>
/// Gets the North gradient operator

16
src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs

@ -23,27 +23,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc/>
public override DenseMatrix<float> North => KirshKernels.KirschNorth;
public override DenseMatrix<float> North => KirschKernels.KirschNorth;
/// <inheritdoc/>
public override DenseMatrix<float> NorthWest => KirshKernels.KirschNorthWest;
public override DenseMatrix<float> NorthWest => KirschKernels.KirschNorthWest;
/// <inheritdoc/>
public override DenseMatrix<float> West => KirshKernels.KirschWest;
public override DenseMatrix<float> West => KirschKernels.KirschWest;
/// <inheritdoc/>
public override DenseMatrix<float> SouthWest => KirshKernels.KirschSouthWest;
public override DenseMatrix<float> SouthWest => KirschKernels.KirschSouthWest;
/// <inheritdoc/>
public override DenseMatrix<float> South => KirshKernels.KirschSouth;
public override DenseMatrix<float> South => KirschKernels.KirschSouth;
/// <inheritdoc/>
public override DenseMatrix<float> SouthEast => KirshKernels.KirschSouthEast;
public override DenseMatrix<float> SouthEast => KirschKernels.KirschSouthEast;
/// <inheritdoc/>
public override DenseMatrix<float> East => KirshKernels.KirschEast;
public override DenseMatrix<float> East => KirschKernels.KirschEast;
/// <inheritdoc/>
public override DenseMatrix<float> NorthEast => KirshKernels.KirschNorthEast;
public override DenseMatrix<float> NorthEast => KirschKernels.KirschNorthEast;
}
}

13
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs

@ -15,11 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class OctreeQuantizer : IQuantizer
{
/// <summary>
/// The default maximum number of colors to use when quantizing the image.
/// </summary>
public const int DefaultMaxColors = 256;
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
@ -42,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public OctreeQuantizer(bool dither)
: this(GetDiffuser(dither), DefaultMaxColors)
: this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{
}
@ -51,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public OctreeQuantizer(IErrorDiffuser diffuser)
: this(diffuser, DefaultMaxColors)
: this(diffuser, QuantizerConstants.MaxColors)
{
}
@ -63,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors)
{
this.Diffuser = diffuser;
this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
/// <inheritdoc />
@ -84,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel>
{
maxColors = maxColors.Clamp(1, DefaultMaxColors);
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new OctreeFrameQuantizer<TPixel>(this, maxColors);
}

19
src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@ -23,27 +22,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
private readonly TPixel[] palette;
/// <summary>
/// The vector representation of the image palette.
/// </summary>
private readonly Vector4[] paletteVector;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="quantizer">The palette quantizer.</param>
/// <param name="colors">An array of all colors in the palette.</param>
public PaletteFrameQuantizer(Configuration configuration, PaletteQuantizer quantizer, TPixel[] colors)
: base(quantizer, true)
{
// TODO: Why is this value constrained? Gif has limitations but theoretically
// we might want to reduce the palette of an image to greater than that limitation.
Guard.MustBeBetweenOrEqualTo(colors.Length, 1, 256, nameof(colors));
this.palette = colors;
this.paletteVector = new Vector4[this.palette.Length];
PixelOperations<TPixel>.Instance.ToScaledVector4(configuration, this.palette, this.paletteVector);
}
public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors)
: base(quantizer, true) => this.palette = colors;
/// <inheritdoc/>
protected override void SecondPass(

52
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs

@ -8,18 +8,18 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// Allows the quantization of images pixels using web safe colors defined in the CSS Color Module Level 4.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/> Override this class to provide your own palette.
/// Allows the quantization of images pixels using color palettes.
/// Override this class to provide your own palette.
/// <para>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering and the <see cref="NamedColors{TPixel}.WebSafePalette"/>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering.
/// </para>
/// </summary>
public class PaletteQuantizer : IQuantizer
public abstract class PaletteQuantizer : IQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
public PaletteQuantizer()
protected PaletteQuantizer()
: this(true)
{
}
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public PaletteQuantizer(bool dither)
protected PaletteQuantizer(bool dither)
: this(GetDiffuser(dither))
{
}
@ -37,42 +37,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser;
protected PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser;
/// <inheritdoc />
public IErrorDiffuser Diffuser { get; }
/// <inheritdoc />
public virtual IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
where TPixel : struct, IPixel<TPixel>
=> this.CreateFrameQuantizer(configuration, () => NamedColors<TPixel>.WebSafePalette);
public abstract IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
where TPixel : struct, IPixel<TPixel>;
/// <inheritdoc/>
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
public abstract IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Creates the generic frame quantizer.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="palette">The color palette.</param>
/// <param name="maxColors">The maximum number of colors to hold in the color palette.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/></returns>
protected IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, TPixel[] palette, int maxColors)
where TPixel : struct, IPixel<TPixel>
{
TPixel[] websafe = NamedColors<TPixel>.WebSafePalette;
int max = Math.Min(maxColors, websafe.Length);
int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length));
if (max != websafe.Length)
if (max != palette.Length)
{
return this.CreateFrameQuantizer(configuration, () => NamedColors<TPixel>.WebSafePalette.AsSpan(0, max).ToArray());
return new PaletteFrameQuantizer<TPixel>(this, palette.AsSpan(0, max).ToArray());
}
return this.CreateFrameQuantizer(configuration, () => websafe);
return new PaletteFrameQuantizer<TPixel>(this, palette);
}
/// <summary>
/// Gets the palette to use to quantize the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations</param>
/// <param name="paletteFunction">The method to return the palette.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/></returns>
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, Func<TPixel[]> paletteFunction)
where TPixel : struct, IPixel<TPixel>
=> new PaletteFrameQuantizer<TPixel>(configuration, this, paletteFunction.Invoke());
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

110
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs

@ -0,0 +1,110 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// A generic palette quantizer.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class PaletteQuantizer<TPixel> : IQuantizer
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The color palette to use.</param>
public PaletteQuantizer(TPixel[] palette)
: this(palette, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The color palette to use.</param>
/// <param name="dither">Whether to apply dithering to the output image</param>
public PaletteQuantizer(TPixel[] palette, bool dither)
: this(palette, GetDiffuser(dither))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The color palette to use.</param>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser)
{
Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette));
this.Palette = palette;
this.Diffuser = diffuser;
}
/// <inheritdoc/>
public IErrorDiffuser Diffuser { get; }
/// <summary>
/// Gets the palette.
/// </summary>
public TPixel[] Palette { get; }
/// <summary>
/// Creates the generic frame quantizer.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/>.</returns>
public IFrameQuantizer<TPixel> CreateFrameQuantizer(Configuration configuration)
=> ((IQuantizer)this).CreateFrameQuantizer<TPixel>(configuration);
/// <summary>
/// Creates the generic frame quantizer.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to configure internal operations.</param>
/// <param name="maxColors">The maximum number of colors to hold in the color palette.</param>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/>.</returns>
public IFrameQuantizer<TPixel> CreateFrameQuantizer(Configuration configuration, int maxColors)
=> ((IQuantizer)this).CreateFrameQuantizer<TPixel>(configuration, maxColors);
/// <inheritdoc/>
IFrameQuantizer<TPixel1> IQuantizer.CreateFrameQuantizer<TPixel1>(Configuration configuration)
{
if (!typeof(TPixel).Equals(typeof(TPixel1)))
{
throw new InvalidOperationException("Generic method type must be the same as class type.");
}
TPixel[] paletteRef = this.Palette;
return new PaletteFrameQuantizer<TPixel1>(this, Unsafe.As<TPixel[], TPixel1[]>(ref paletteRef));
}
/// <inheritdoc/>
IFrameQuantizer<TPixel1> IQuantizer.CreateFrameQuantizer<TPixel1>(Configuration configuration, int maxColors)
{
if (!typeof(TPixel).Equals(typeof(TPixel1)))
{
throw new InvalidOperationException("Generic method type must be the same as class type.");
}
TPixel[] paletteRef = this.Palette;
TPixel1[] castPalette = Unsafe.As<TPixel[], TPixel1[]>(ref paletteRef);
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
int max = Math.Min(maxColors, castPalette.Length);
if (max != castPalette.Length)
{
return new PaletteFrameQuantizer<TPixel1>(this, castPalette.AsSpan(0, max).ToArray());
}
return new PaletteFrameQuantizer<TPixel1>(this, castPalette);
}
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

21
src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// Contains color quantization specific constants.
/// </summary>
internal static class QuantizerConstants
{
/// <summary>
/// The minimum number of colors to use when quantizing an image.
/// </summary>
public const int MinColors = 1;
/// <summary>
/// The maximum number of colors to use when quantizing an image.
/// </summary>
public const int MaxColors = 256;
}
}

47
src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs

@ -0,0 +1,47 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// A palette quantizer consisting of web safe colors as defined in the CSS Color Module Level 4.
/// </summary>
public class WebSafePaletteQuantizer : PaletteQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
public WebSafePaletteQuantizer()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WebSafePaletteQuantizer(bool dither)
: base(dither)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WebSafePaletteQuantizer(IErrorDiffuser diffuser)
: base(diffuser)
{
}
/// <inheritdoc />
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
=> this.CreateFrameQuantizer<TPixel>(configuration, NamedColors<TPixel>.WebSafePalette.Length);
/// <inheritdoc/>
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
=> this.CreateFrameQuantizer(configuration, NamedColors<TPixel>.WebSafePalette, maxColors);
}
}

48
src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs

@ -0,0 +1,48 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
/// A palette quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821.
/// The hex codes were collected and defined by Nicholas Rougeux <see href="https://www.c82.net/werner"/>
/// </summary>
public class WernerPaletteQuantizer : PaletteQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
public WernerPaletteQuantizer()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WernerPaletteQuantizer(bool dither)
: base(dither)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WernerPaletteQuantizer(IErrorDiffuser diffuser)
: base(diffuser)
{
}
/// <inheritdoc />
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration)
=> this.CreateFrameQuantizer<TPixel>(configuration, NamedColors<TPixel>.WernerPalette.Length);
/// <inheritdoc/>
public override IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
=> this.CreateFrameQuantizer(configuration, NamedColors<TPixel>.WernerPalette, maxColors);
}
}

13
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs

@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
public class WuQuantizer : IQuantizer
{
/// <summary>
/// The default maximum number of colors to use when quantizing the image.
/// </summary>
public const int DefaultMaxColors = 256;
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
@ -41,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WuQuantizer(bool dither)
: this(GetDiffuser(dither), DefaultMaxColors)
: this(GetDiffuser(dither), QuantizerConstants.MaxColors)
{
}
@ -50,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WuQuantizer(IErrorDiffuser diffuser)
: this(diffuser, DefaultMaxColors)
: this(diffuser, QuantizerConstants.MaxColors)
{
}
@ -62,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public WuQuantizer(IErrorDiffuser diffuser, int maxColors)
{
this.Diffuser = diffuser;
this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
/// <inheritdoc />
@ -83,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>(Configuration configuration, int maxColors)
where TPixel : struct, IPixel<TPixel>
{
maxColors = maxColors.Clamp(1, DefaultMaxColors);
maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
return new WuFrameQuantizer<TPixel>(this, maxColors);
}

2
tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public void GifCore()
{
// Try to get as close to System.Drawing's output as possible
var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) };
var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
using (var memoryStream = new MemoryStream())
{
this.bmpCore.SaveAsGif(memoryStream, options);

2
tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
this.ForEachImageSharpImage((img, ms) =>
{
// Try to get as close to System.Drawing's output as possible
var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) };
var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
img.Save(ms, options); return null;
});
}

4
tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{
using (var memoryStream = new MemoryStream())
{
var options = new PngEncoder { Quantizer = KnownQuantizers.Palette };
var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{
using (var memoryStream = new MemoryStream())
{
var options = new PngEncoder { Quantizer = new PaletteQuantizer(false) };
var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(false) };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}

133
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs

@ -0,0 +1,133 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_CopyTo1x1
{
private Block8x8F block;
private Buffer2D<float> buffer;
private BufferArea<float> destArea;
[GlobalSetup]
public void Setup()
{
if (!SimdUtils.IsAvx2CompatibleArchitecture)
{
throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support.");
}
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500);
this.destArea = this.buffer.GetArea(200, 100, 64, 64);
}
[Benchmark(Baseline = true)]
public void Original()
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this.block);
ref byte destBase = ref Unsafe.As<float, byte>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride * sizeof(float);
CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
CopyRowImpl(ref selfBase, ref destBase, destStride, 2);
CopyRowImpl(ref selfBase, ref destBase, destStride, 3);
CopyRowImpl(ref selfBase, ref destBase, destStride, 4);
CopyRowImpl(ref selfBase, ref destBase, destStride, 5);
CopyRowImpl(ref selfBase, ref destBase, destStride, 6);
CopyRowImpl(ref selfBase, ref destBase, destStride, 7);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row)
{
ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float));
ref byte d = ref Unsafe.Add(ref destBase, row * destStride);
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
[Benchmark]
public void UseVector8()
{
ref Block8x8F s = ref this.block;
ref float origin = ref this.destArea.GetReferenceToOrigin();
int stride = this.destArea.Stride;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
Vector<float> row0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
Vector<float> row1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
Vector<float> row2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
Vector<float> row3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
Vector<float> row4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
Vector<float> row5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
Vector<float> row6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
Vector<float> row7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
d0 = row0;
d1 = row1;
d2 = row2;
d3 = row3;
d4 = row4;
d5 = row5;
d6 = row6;
d7 = row7;
}
[Benchmark]
public void UseVector8_V2()
{
ref Block8x8F s = ref this.block;
ref float origin = ref this.destArea.GetReferenceToOrigin();
int stride = this.destArea.Stride;
ref Vector<float> d0 = ref Unsafe.As<float, Vector<float>>(ref origin);
ref Vector<float> d1 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride));
ref Vector<float> d2 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 2));
ref Vector<float> d3 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 3));
ref Vector<float> d4 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 4));
ref Vector<float> d5 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 5));
ref Vector<float> d6 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 6));
ref Vector<float> d7 = ref Unsafe.As<float, Vector<float>>(ref Unsafe.Add(ref origin, stride * 7));
d0 = Unsafe.As<Vector4, Vector<float>>(ref s.V0L);
d1 = Unsafe.As<Vector4, Vector<float>>(ref s.V1L);
d2 = Unsafe.As<Vector4, Vector<float>>(ref s.V2L);
d3 = Unsafe.As<Vector4, Vector<float>>(ref s.V3L);
d4 = Unsafe.As<Vector4, Vector<float>>(ref s.V4L);
d5 = Unsafe.As<Vector4, Vector<float>>(ref s.V5L);
d6 = Unsafe.As<Vector4, Vector<float>>(ref s.V6L);
d7 = Unsafe.As<Vector4, Vector<float>>(ref s.V7L);
}
// RESULTS:
//
// Method | Mean | Error | StdDev | Scaled |
// -------------- |---------:|----------:|----------:|-------:|
// Original | 22.53 ns | 0.1660 ns | 0.1553 ns | 1.00 |
// UseVector8 | 21.59 ns | 0.3079 ns | 0.2571 ns | 0.96 |
// UseVector8_V2 | 22.57 ns | 0.1699 ns | 0.1506 ns | 1.00 |
//
// Conclusion:
// Doesn't worth to bother with this
}
}

404
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs

@ -0,0 +1,404 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_CopyTo2x2
{
private Block8x8F block;
private Buffer2D<float> buffer;
private BufferArea<float> destArea;
[GlobalSetup]
public void Setup()
{
this.buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(1000, 500);
this.destArea = this.buffer.GetArea(200, 100, 128, 128);
}
[Benchmark(Baseline = true)]
public void Original()
{
ref float destBase = ref this.destArea.GetReferenceToOrigin();
int destStride = this.destArea.Stride;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride)
{
ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride);
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X;
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y;
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z;
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W;
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W;
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W;
}
[Benchmark]
public void Original_V2()
{
ref float destBase = ref this.destArea.GetReferenceToOrigin();
int destStride = this.destArea.Stride;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride)
{
ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1);
ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride);
Unsafe.Add(ref dest0, 0) = selfLeft.X;
Unsafe.Add(ref dest0, 1) = selfLeft.X;
Unsafe.Add(ref dest0, 2) = selfLeft.Y;
Unsafe.Add(ref dest0, 3) = selfLeft.Y;
Unsafe.Add(ref dest0, 4) = selfLeft.Z;
Unsafe.Add(ref dest0, 5) = selfLeft.Z;
Unsafe.Add(ref dest0, 6) = selfLeft.W;
Unsafe.Add(ref dest0, 7) = selfLeft.W;
ref float dest1 = ref Unsafe.Add(ref dest0, 8);
Unsafe.Add(ref dest1, 0) = selfRight.X;
Unsafe.Add(ref dest1, 1) = selfRight.X;
Unsafe.Add(ref dest1, 2) = selfRight.Y;
Unsafe.Add(ref dest1, 3) = selfRight.Y;
Unsafe.Add(ref dest1, 4) = selfRight.Z;
Unsafe.Add(ref dest1, 5) = selfRight.Z;
Unsafe.Add(ref dest1, 6) = selfRight.W;
Unsafe.Add(ref dest1, 7) = selfRight.W;
ref float dest2 = ref Unsafe.Add(ref dest0, destStride);
Unsafe.Add(ref dest2, 0) = selfLeft.X;
Unsafe.Add(ref dest2, 1) = selfLeft.X;
Unsafe.Add(ref dest2, 2) = selfLeft.Y;
Unsafe.Add(ref dest2, 3) = selfLeft.Y;
Unsafe.Add(ref dest2, 4) = selfLeft.Z;
Unsafe.Add(ref dest2, 5) = selfLeft.Z;
Unsafe.Add(ref dest2, 6) = selfLeft.W;
Unsafe.Add(ref dest2, 7) = selfLeft.W;
ref float dest3 = ref Unsafe.Add(ref dest2, 8);
Unsafe.Add(ref dest3, 0) = selfRight.X;
Unsafe.Add(ref dest3, 1) = selfRight.X;
Unsafe.Add(ref dest3, 2) = selfRight.Y;
Unsafe.Add(ref dest3, 3) = selfRight.Y;
Unsafe.Add(ref dest3, 4) = selfRight.Z;
Unsafe.Add(ref dest3, 5) = selfRight.Z;
Unsafe.Add(ref dest3, 6) = selfRight.W;
Unsafe.Add(ref dest3, 7) = selfRight.W;
}
[Benchmark]
public void UseVector2()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4);
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4);
var xLeft = new Vector2(sLeft.X);
var yLeft = new Vector2(sLeft.Y);
var zLeft = new Vector2(sLeft.Z);
var wLeft = new Vector2(sLeft.W);
var xRight = new Vector2(sRight.X);
var yRight = new Vector2(sRight.Y);
var zRight = new Vector2(sRight.Z);
var wRight = new Vector2(sRight.W);
dTopLeft = xLeft;
Unsafe.Add(ref dTopLeft, 1) = yLeft;
Unsafe.Add(ref dTopLeft, 2) = zLeft;
Unsafe.Add(ref dTopLeft, 3) = wLeft;
dTopRight = xRight;
Unsafe.Add(ref dTopRight, 1) = yRight;
Unsafe.Add(ref dTopRight, 2) = zRight;
Unsafe.Add(ref dTopRight, 3) = wRight;
dBottomLeft = xLeft;
Unsafe.Add(ref dBottomLeft, 1) = yLeft;
Unsafe.Add(ref dBottomLeft, 2) = zLeft;
Unsafe.Add(ref dBottomLeft, 3) = wLeft;
dBottomRight = xRight;
Unsafe.Add(ref dBottomRight, 1) = yRight;
Unsafe.Add(ref dBottomRight, 2) = zRight;
Unsafe.Add(ref dBottomRight, 3) = wRight;
}
[Benchmark]
public void UseVector4()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4);
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4);
var xLeft = new Vector4(sLeft.X);
var yLeft = new Vector4(sLeft.Y);
var zLeft = new Vector4(sLeft.Z);
var wLeft = new Vector4(sLeft.W);
var xRight = new Vector4(sRight.X);
var yRight = new Vector4(sRight.Y);
var zRight = new Vector4(sRight.Z);
var wRight = new Vector4(sRight.W);
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 3)) = wRight;
}
[Benchmark]
public void UseVector4_SafeRightCorner()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride);
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride);
var xLeft = new Vector4(sLeft.X);
var yLeft = new Vector4(sLeft.Y);
var zLeft = new Vector4(sLeft.Z);
var wLeft = new Vector4(sLeft.W);
var xRight = new Vector4(sRight.X);
var yRight = new Vector4(sRight.Y);
var zRight = new Vector4(sRight.Z);
var wRight = new Vector2(sRight.W);
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = zRight;
Unsafe.Add(ref dTopLeft, 7) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight;
Unsafe.Add(ref dBottomLeft, 7) = wRight;
}
[Benchmark]
public void UseVector4_V2()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref this.destArea.GetReferenceToOrigin());
int destStride = this.destArea.Stride / 2;
ref Block8x8F src = ref this.block;
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride);
WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride)
{
ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
int offset = 2 * row * destStride;
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride));
var xyLeft = new Vector4(sLeft.X);
xyLeft.Z = sLeft.Y;
xyLeft.W = sLeft.Y;
var zwLeft = new Vector4(sLeft.Z);
zwLeft.Z = sLeft.W;
zwLeft.W = sLeft.W;
var xyRight = new Vector4(sRight.X);
xyRight.Z = sRight.Y;
xyRight.W = sRight.Y;
var zwRight = new Vector4(sRight.Z);
zwRight.Z = sRight.W;
zwRight.W = sRight.W;
dTopLeft = xyLeft;
Unsafe.Add(ref dTopLeft, 1) = zwLeft;
Unsafe.Add(ref dTopLeft, 2) = xyRight;
Unsafe.Add(ref dTopLeft, 3) = zwRight;
dBottomLeft = xyLeft;
Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
Unsafe.Add(ref dBottomLeft, 2) = xyRight;
Unsafe.Add(ref dBottomLeft, 3) = zwRight;
}
// RESULTS:
// Method | Mean | Error | StdDev | Scaled | ScaledSD |
// --------------------------- |---------:|----------:|----------:|-------:|---------:|
// Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 |
// Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 |
// UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 |
// UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 |
// UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 |
// UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 |
}
}

6
tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs → tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs

@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.General
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
/// <summary>
/// The goal of this benchmark is to measure the following Jpeg-related scenario:
@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.General
private static readonly Vector4 MinusOne = new Vector4(-1);
private static readonly Vector4 Half = new Vector4(0.5f);
private Block8x8F inputDividend = default(Block8x8F);
private Block8x8F inputDivisior = default(Block8x8F);
private Block8x8F inputDividend;
private Block8x8F inputDivisior;
[GlobalSetup]
public void Setup()

53
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs

@ -0,0 +1,53 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
using System;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_LoadFromInt16
{
private Block8x8 source;
private Block8x8F dest = default;
[GlobalSetup]
public void Setup()
{
if (Vector<float>.Count != 8)
{
throw new NotSupportedException("Vector<float>.Count != 8");
}
for (short i = 0; i < Block8x8F.Size; i++)
{
this.source[i] = i;
}
}
[Benchmark(Baseline = true)]
public void Scalar()
{
this.dest.LoadFromInt16Scalar(ref this.source);
}
[Benchmark]
public void ExtendedAvx2()
{
this.dest.LoadFromInt16ExtendedAvx2(ref this.source);
}
// RESULT:
// Method | Mean | Error | StdDev | Scaled |
// ------------- |---------:|----------:|----------:|-------:|
// Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 |
// ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 |
}
}

7
tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs → tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs

@ -1,4 +1,7 @@
// ReSharper disable InconsistentNaming
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
using System;
using System.Numerics;
@ -8,7 +11,7 @@ using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Benchmarks.General
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
public class Block8x8F_Round
{

59
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs

@ -1,59 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Drawing;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using CoreSize = SixLabors.Primitives.Size;
using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class DecodeJpeg : BenchmarkBase
{
private byte[] jpegBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)]
public string TestImage { get; set; }
[GlobalSetup]
public void ReadImages()
{
if (this.jpegBytes == null)
{
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")]
public Size JpegSystemDrawing()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = SDImage.FromStream(memoryStream))
{
return image.Size;
}
}
}
[Benchmark(Description = "Decode Jpeg - ImageSharp")]
public CoreSize JpegImageSharp()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder()))
{
return new CoreSize(image.Width, image.Height);
}
}
}
}
}

35
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SDImage = System.Drawing.Image;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class DecodeJpegMultiple : MultiImageBenchmarkBase
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles => new[]
{
"Jpg/baseline",
"Jpg/progressive",
};
protected override IEnumerable<string> SearchPatterns => new[] { "*.jpg" };
[Benchmark(Description = "DecodeJpegMultiple - ImageSharp")]
public void DecodeJpegImageSharp()
{
this.ForEachStream(ms => Image.Load<Rgba32>(ms, new JpegDecoder()));
}
[Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")]
public void DecodeJpegSystemDrawing()
{
this.ForEachStream(SDImage.FromStream);
}
}
}

48
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs

@ -0,0 +1,48 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using SDImage = System.Drawing.Image;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
/// <summary>
/// An expensive Jpeg benchmark, running on a wide range of input images, showing aggregate results.
/// </summary>
[Config(typeof(MultiImageBenchmarkBase.Config))]
public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles =>
new[]
{
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
};
[Params(InputImageCategory.AllImages)]
public override InputImageCategory InputCategory { get; set; }
[Benchmark]
public void ImageSharp()
{
this.ForEachStream(ms => Image.Load<Rgba32>(ms, new JpegDecoder()));
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
this.ForEachStream(SDImage.FromStream);
}
}
}

119
tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs

@ -0,0 +1,119 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Drawing;
using System.IO;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
using CoreSize = SixLabors.Primitives.Size;
using SDImage = System.Drawing.Image;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
/// <summary>
/// Image-specific Jpeg benchmarks
/// </summary>
[Config(typeof(Config.ShortClr))]
public class DecodeJpeg_ImageSpecific
{
public class Config : ManualConfig
{
public Config()
{
// Uncomment if you want to use any of the diagnoser
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
}
public class ShortClr : Benchmarks.Config
{
public ShortClr()
{
this.Add(
//Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3),
Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3)
);
}
}
}
private byte[] jpegBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
// The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr"
// is almost the same as the result for Jpeg420Exif,
// which proves that the execution time for the most common YCbCr 420 path scales linearly.
//
// TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr
)]
public string TestImage { get; set; }
[GlobalSetup]
public void ReadImages()
{
if (this.jpegBytes == null)
{
this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath);
}
}
[Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")]
public Size JpegSystemDrawing()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = SDImage.FromStream(memoryStream))
{
return image.Size;
}
}
}
[Benchmark(Description = "Decode Jpeg - ImageSharp")]
public CoreSize JpegImageSharp()
{
using (var memoryStream = new MemoryStream(this.jpegBytes))
{
using (var image = Image.Load<Rgba32>(memoryStream, new JpegDecoder(){ IgnoreMetadata = true}))
{
return new CoreSize(image.Width, image.Height);
}
}
}
// RESULTS (2018 November 4):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | TestImage | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
// ------------------------------- |-------------------------------------------- |-----------:|-----------:|----------:|-------:|---------:|----------:|---------:|---------:|------------:|
// 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.117 ms | 0.3923 ms | 0.0222 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.126 ms | 0.6023 ms | 0.0340 ms | 2.96 | 0.01 | - | - | - | 19.97 KB |
// | | | | | | | | | | |
// 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 17.063 ms | 2.6096 ms | 0.1474 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 41.366 ms | 1.0115 ms | 0.0572 ms | 2.42 | 0.02 | - | - | - | 21.94 KB |
// | | | | | | | | | | |
// 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 428.282 ms | 94.9163 ms | 5.3629 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 386.698 ms | 33.0065 ms | 1.8649 ms | 0.90 | 0.01 | 125.0000 | 125.0000 | 125.0000 | 35186.97 KB |
// | | | | | | | | | | |
// 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB |
// 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB |
}
}

86
tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs

@ -1,86 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
using System;
using System.IO;
using SixLabors.ImageSharp.Tests;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using SixLabors.ImageSharp.Processing;
using SDImage = System.Drawing.Image;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class LoadResizeSave : BenchmarkBase
{
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
private byte[] sourceBytes;
private byte[] destBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(
TestImages.Jpeg.Baseline.Jpeg420Exif
//, TestImages.Jpeg.Baseline.Calliphora
)]
public string TestImage { get; set; }
[Params(false, true)]
public bool EnableParallelExecution { get; set; }
[GlobalSetup]
public void Setup()
{
this.configuration.MaxDegreeOfParallelism =
this.EnableParallelExecution ? Environment.ProcessorCount : 1;
if (this.sourceBytes == null)
{
this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath);
}
if (this.destBytes == null)
{
this.destBytes = new byte[this.sourceBytes.Length];
}
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
using (var sourceStream = new MemoryStream(this.sourceBytes))
using (var destStream = new MemoryStream(this.destBytes))
using (var source = SDImage.FromStream(sourceStream))
using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
{
using (var graphics = Graphics.FromImage(destination))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(source, 0, 0, 400, 400);
}
destination.Save(destStream, ImageFormat.Jpeg);
}
}
[Benchmark]
public void ImageSharp()
{
var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true });
using (source)
using (var destStream = new MemoryStream(this.destBytes))
{
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
source.SaveAsJpeg(destStream);
}
}
}
}

96
tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs

@ -0,0 +1,96 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(MultiImageBenchmarkBase.Config))]
public class LoadResizeSave_Aggregate : MultiImageBenchmarkBase
{
protected override IEnumerable<string> InputImageSubfoldersOrFiles =>
new[]
{
TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr,
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr,
TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr,
};
[Params(InputImageCategory.AllImages)]
public override InputImageCategory InputCategory { get; set; }
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
private byte[] destBytes;
public override void Setup()
{
base.Setup();
this.configuration.MaxDegreeOfParallelism = 1;
const int MaxOutputSizeInBytes = 2 * 1024 * 1024; // ~2 MB
this.destBytes = new byte[MaxOutputSizeInBytes];
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
this.ForEachStream(
sourceStream =>
{
using (var destStream = new MemoryStream(this.destBytes))
using (var source = System.Drawing.Image.FromStream(sourceStream))
using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
{
using (var g = Graphics.FromImage(destination))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(source, 0, 0, 400, 400);
}
destination.Save(destStream, ImageFormat.Jpeg);
}
return null;
});
}
[Benchmark]
public void ImageSharp()
{
this.ForEachStream(
sourceStream =>
{
using (var source = Image.Load<Rgba32>(
this.configuration,
sourceStream,
new JpegDecoder { IgnoreMetadata = true }))
{
using (var destStream = new MemoryStream(this.destBytes))
{
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
source.SaveAsJpeg(destStream);
}
}
return null;
});
}
}
}

107
tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs

@ -0,0 +1,107 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using BenchmarkDotNet.Attributes;
using System;
using System.IO;
using SixLabors.ImageSharp.Tests;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using SixLabors.ImageSharp.Processing;
using SDImage = System.Drawing.Image;
using SixLabors.ImageSharp.Formats.Jpeg;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
{
[Config(typeof(Config.ShortClr))]
public class LoadResizeSave_ImageSpecific
{
private readonly Configuration configuration = new Configuration(new JpegConfigurationModule());
private byte[] sourceBytes;
private byte[] destBytes;
private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage);
[Params(
TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr,
TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr,
TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr
)]
public string TestImage { get; set; }
[Params(false, true)]
public bool ParallelExec { get; set; }
[GlobalSetup]
public void Setup()
{
this.configuration.MaxDegreeOfParallelism =
this.ParallelExec ? Environment.ProcessorCount : 1;
this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath);
this.destBytes = new byte[this.sourceBytes.Length * 2];
}
[Benchmark(Baseline = true)]
public void SystemDrawing()
{
using (var sourceStream = new MemoryStream(this.sourceBytes))
using (var destStream = new MemoryStream(this.destBytes))
using (var source = SDImage.FromStream(sourceStream))
using (var destination = new Bitmap(source.Width / 4, source.Height / 4))
{
using (var g = Graphics.FromImage(destination))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(source, 0, 0, 400, 400);
}
destination.Save(destStream, ImageFormat.Jpeg);
}
}
[Benchmark]
public void ImageSharp()
{
var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true });
using (source)
using (var destStream = new MemoryStream(this.destBytes))
{
source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4));
source.SaveAsJpeg(destStream);
}
}
// RESULTS (2018 October 31):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-ZPEZGV : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-SGOCJT : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | TestImage | ParallelExec | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated |
// -------------- |-------- |----------------------------- |------------- |----------:|----------:|----------:|-------:|---------:|---------:|----------:|
// SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | False | 64.88 ms | 3.735 ms | 0.2110 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
// ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | False | 129.53 ms | 23.423 ms | 1.3234 ms | 2.00 | 0.02 | - | 50.09 KB |
// | | | | | | | | | | |
// SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | False | 65.87 ms | 10.488 ms | 0.5926 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
// ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | False | 92.00 ms | 7.241 ms | 0.4091 ms | 1.40 | 0.01 | - | 46.36 KB |
// | | | | | | | | | | |
// SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | True | 64.23 ms | 5.998 ms | 0.3389 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB |
// ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | True | 82.63 ms | 29.320 ms | 1.6566 ms | 1.29 | 0.02 | - | 57.59 KB |
// | | | | | | | | | | |
// SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | True | 64.20 ms | 6.560 ms | 0.3707 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB |
// ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | True | 68.08 ms | 18.376 ms | 1.0383 ms | 1.06 | 0.01 | - | 50.49 KB |
}
}

32
tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs

@ -3,6 +3,9 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Benchmarks.Codecs
@ -20,8 +23,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
using CoreImage = ImageSharp.Image;
public abstract class MultiImageBenchmarkBase : BenchmarkBase
public abstract class MultiImageBenchmarkBase
{
public class Config : ManualConfig
{
public Config()
{
// Uncomment if you want to use any of the diagnoser
this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser());
}
public class ShortClr : Benchmarks.Config
{
public ShortClr()
{
this.Add(
Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithTargetCount(2)
);
}
}
}
protected Dictionary<string, byte[]> FileNamesToBytes = new Dictionary<string, byte[]>();
protected Dictionary<string, Image<Rgba32>> FileNamesToImageSharpImages = new Dictionary<string, Image<Rgba32>>();
@ -49,7 +71,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
/// <summary>
/// Gets the file names containing these strings are substrings are not processed by the benchmark.
/// </summary>
protected IEnumerable<string> ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
protected virtual IEnumerable<string> ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" };
/// <summary>
/// Enumerates folders containing files OR files to be processed by the benchmark.
@ -87,7 +109,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
protected abstract IEnumerable<string> InputImageSubfoldersOrFiles { get; }
[GlobalSetup]
public void ReadImages()
public virtual void Setup()
{
if (!Vector.IsHardwareAccelerated)
{
@ -107,11 +129,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
continue;
}
string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray();
string[] allFiles =
this.SearchPatterns.SelectMany(
f =>
Directory.EnumerateFiles(path, f, SearchOption.AllDirectories)
.Where(fn => !this.ExcludeSubstringsInFileNames.Any(w => fn.ToLower().Contains(w)))).ToArray();
.Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray();
foreach (string fn in allFiles)
{

74
tests/ImageSharp.Benchmarks/Samplers/Resize.cs

@ -22,15 +22,17 @@ namespace SixLabors.ImageSharp.Benchmarks
private Bitmap sourceBitmap;
public const int SourceSize = 3032;
[Params(3032)]
public int SourceSize { get; set; }
public const int DestSize = 400;
[Params(400)]
public int DestSize { get; set; }
[GlobalSetup]
public void Setup()
{
this.sourceImage = new Image<Rgba32>(this.Configuration, SourceSize, SourceSize);
this.sourceBitmap = new Bitmap(SourceSize, SourceSize);
this.sourceImage = new Image<Rgba32>(this.Configuration, this.SourceSize, this.SourceSize);
this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize);
}
[GlobalCleanup]
@ -43,14 +45,17 @@ namespace SixLabors.ImageSharp.Benchmarks
[Benchmark(Baseline = true)]
public int SystemDrawing()
{
using (var destination = new Bitmap(DestSize, DestSize))
using (var destination = new Bitmap(this.DestSize, this.DestSize))
{
using (var graphics = Graphics.FromImage(destination))
using (var g = Graphics.FromImage(destination))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.DrawImage(this.sourceBitmap, 0, 0, DestSize, DestSize);
g.CompositingMode = CompositingMode.SourceCopy;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize);
}
return destination.Width;
@ -83,15 +88,60 @@ namespace SixLabors.ImageSharp.Benchmarks
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{
ctx.Resize(DestSize, DestSize, KnownResamplers.Bicubic);
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic);
}
// RESULTS (2018 October):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
// SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B |
// | | | | | | | | | |
// SystemDrawing | Core | 3032 | 400 | 100.37 ms | 18.479 ms | 1.0441 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 73.03 ms | 10.540 ms | 0.5955 ms | 0.73 | 0.01 | 21368 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 22.59 ms | 4.863 ms | 0.2748 ms | 0.23 | 0.00 | 25220 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 21.10 ms | 23.362 ms | 1.3200 ms | 0.21 | 0.01 | 25539 B |
}
public class Resize_BicubicCompand : ResizeBenchmarkBase
{
protected override void ExecuteResizeOperation(IImageProcessingContext<Rgba32> ctx)
{
ctx.Resize(DestSize, DestSize, KnownResamplers.Bicubic, true);
ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true);
}
// RESULTS (2018 October):
//
// BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134
// Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
// Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC
// .NET Core SDK=2.1.403
// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
// Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0
// Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT
//
// Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated |
// ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:|
// SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 156.83 ms | 28.631 ms | 1.6177 ms | 1.56 | 0.02 | 21856 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 53.43 ms | 38.493 ms | 2.1749 ms | 0.53 | 0.02 | 28512 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 B |
// | | | | | | | | | |
// SystemDrawing | Core | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 B |
// 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B |
// 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B |
// 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B |
}
}

2
tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

@ -11,7 +11,7 @@
<Authors>James Jackson-South and contributors</Authors>
<Company>James Jackson-South</Company>
<RootNamespace>SixLabors.ImageSharp.Sandbox46</RootNamespace>
<LangVersion>7.2</LangVersion>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />

5
tests/ImageSharp.Sandbox46/Program.cs

@ -4,6 +4,7 @@
// </copyright>
using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations;
using SixLabors.ImageSharp.Tests.ProfilingBenchmarks;
namespace SixLabors.ImageSharp.Sandbox46
{
@ -62,8 +63,8 @@ namespace SixLabors.ImageSharp.Sandbox46
private static void RunDecodeJpegProfilingTests()
{
Console.WriteLine("RunDecodeJpegProfilingTests...");
var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput());
foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData)
var benchmarks = new JpegBenchmarks(new ConsoleOutput());
foreach (object[] data in JpegBenchmarks.DecodeJpegData)
{
string fileName = (string)data[0];
benchmarks.DecodeJpeg(fileName);

21
tests/ImageSharp.Tests/Common/SimdUtilsTests.cs

@ -257,6 +257,27 @@ namespace SixLabors.ImageSharp.Tests.Common
);
}
[Theory]
[InlineData(1234)]
public void ExtendedIntrinsics_ConvertToSingle(short scale)
{
int n = Vector<float>.Count;
short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale);
float[] fData = sData.Select(u => (float)u).ToArray();
var source = new Vector<short>(sData);
var expected1 = new Vector<float>(fData, 0);
var expected2 = new Vector<float>(fData, n);
// Act:
SimdUtils.ExtendedIntrinsics.ConvertToSingle(source, out Vector<float> actual1, out Vector<float> actual2);
// Assert:
Assert.Equal(expected1, actual1);
Assert.Equal(expected2, actual2);
}
[Theory]
[MemberData(nameof(ArbitraryArraySizes))]
public void BulkConvertNormalizedFloatToByteClampOverflows(int count)

35
tests/ImageSharp.Tests/ComplexIntegrationTests.cs

@ -1,35 +0,0 @@
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
/// <summary>
/// Might be useful to catch complex bugs
/// </summary>
public class ComplexIntegrationTests
{
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)]
[WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)]
[WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)]
[WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)]
public void LoadResizeSave<TPixel>(TestImageProvider<TPixel> provider, int quality, JpegSubsample subsample)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })))
{
image.MetaData.ExifProfile = null; // Reduce the size of the file
JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality };
provider.Utility.TestName += $"{subsample}_Q{quality}";
provider.Utility.SaveTestOutputFile(image, "png");
provider.Utility.SaveTestOutputFile(image, "jpg", options);
}
}
}
}

3
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -67,7 +67,8 @@ namespace SixLabors.ImageSharp.Tests
new TheoryData<string>
{
nameof(KnownQuantizers.Octree),
nameof(KnownQuantizers.Palette),
nameof(KnownQuantizers.WebSafe),
nameof(KnownQuantizers.Werner),
nameof(KnownQuantizers.Wu)
};

33
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -1,20 +1,20 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
using System.IO;
using SixLabors.ImageSharp.Advanced;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
using System.Collections.Generic;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
public class GifDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
@ -70,6 +70,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
}
}
[Fact]
public unsafe void Decode_NonTerminatedFinalFrame()
{
var testFile = TestFile.Create(TestImages.Gif.Rings);
int length = testFile.Bytes.Length - 2;
fixed (byte* data = testFile.Bytes.AsSpan(0, length))
{
using (var stream = new UnmanagedMemoryStream(data, length))
{
var decoder = new GifDecoder();
using (Image<Rgba32> image = decoder.Decode<Rgba32>(Configuration.Default, stream))
{
Assert.Equal((200, 200), (image.Width, image.Height));
}
}
}
}
[Theory]
[MemberData(nameof(RatioFiles))]
public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)

4
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
public class GifEncoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.001F);
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0015F);
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit>
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
// Use the palette quantizer without dithering to ensure results
// are consistant
Quantizer = new PaletteQuantizer(false)
Quantizer = new WebSafePaletteQuantizer(false)
};
// Always save as we need to compare the encoded output.

22
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs

@ -7,14 +7,16 @@
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.Memory;
using SixLabors.Primitives;
using Xunit;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
public partial class Block8x8FTests : JpegFixture
public partial class Block8x8FTests
{
public class CopyToBufferArea : JpegFixture
{
@ -37,17 +39,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
// TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.
[Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")]
//[Fact]
public void Unscaled()
[Fact]
public void Copy1x1Scale()
{
Block8x8F block = CreateRandomFloatBlock(0, 100);
using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20))
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20, AllocationOptions.Clean))
{
BufferArea<float> area = buffer.GetArea(5, 10, 8, 8);
block.CopyTo(area);
block.Copy1x1Scale(area);
Assert.Equal(block[0, 0], buffer[5, 10]);
Assert.Equal(block[1, 0], buffer[6, 10]);
@ -59,22 +59,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
// TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.
[Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")]
//[Theory]
[Theory]
[InlineData(1, 1)]
[InlineData(1, 2)]
[InlineData(2, 1)]
[InlineData(2, 2)]
[InlineData(4, 2)]
[InlineData(4, 4)]
public void Scaled(int horizontalFactor, int verticalFactor)
public void CopyTo(int horizontalFactor, int verticalFactor)
{
Block8x8F block = CreateRandomFloatBlock(0, 100);
var start = new Point(50, 50);
using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100))
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100, AllocationOptions.Clean))
{
BufferArea<float> area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor);
block.CopyTo(area, horizontalFactor, verticalFactor);

42
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -408,5 +408,47 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(original[i] * 42f, actual[i]);
}
}
[Fact]
public void LoadFromUInt16Scalar()
{
if (this.SkipOnNonAvx2Runner())
{
return;
}
short[] data = Create8x8ShortData();
var source = new Block8x8(data);
Block8x8F dest = default;
dest.LoadFromInt16Scalar(ref source);
for (int i = 0; i < Block8x8F.Size; i++)
{
Assert.Equal((float)data[i], dest[i]);
}
}
[Fact]
public void LoadFromUInt16ExtendedAvx2()
{
if (this.SkipOnNonAvx2Runner())
{
return;
}
short[] data = Create8x8ShortData();
var source = new Block8x8(data);
Block8x8F dest = default;
dest.LoadFromInt16ExtendedAvx2(ref source);
for (int i = 0; i < Block8x8F.Size; i++)
{
Assert.Equal((float)data[i], dest[i]);
}
}
}
}

7
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

@ -58,7 +58,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{
for (int j = 0; j < 8; j++)
{
result[i * 8 + j] = (short)(i * 10 + j);
short val = (short)(i * 10 + j);
if ((i + j) % 2 == 0)
{
val *= -1;
}
result[i * 8 + j] = val;
}
}
return result;

3
tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs

@ -67,9 +67,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
for (int y = 0; y < result.HeightInBlocks; y++)
{
Span<Block8x8> blockRow = c.SpectralBlocks.GetRowSpan(y);
for (int x = 0; x < result.WidthInBlocks; x++)
{
short[] data = c.GetBlockReference(x, y).ToArray();
short[] data = blockRow[x].ToArray();
result.MakeBlock(data, y, x);
}
}

25
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Memory;
using Xunit;
// ReSharper disable InconsistentNaming
@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests
}
[Fact]
public void Configuration_Width_Height_BackroundColor()
public void Configuration_Width_Height_BackgroundColor()
{
Configuration configuration = Configuration.Default.Clone();
Rgba32 color = Rgba32.Aquamarine;
@ -61,6 +64,26 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(configuration, image.GetConfiguration());
}
}
[Fact]
public void CreateUninitialized()
{
Configuration configuration = Configuration.Default.Clone();
byte dirtyValue = 123;
configuration.MemoryAllocator = new TestMemoryAllocator(dirtyValue);
var metadata = new ImageMetaData();
using (Image<Gray8> image = Image.CreateUninitialized<Gray8>(configuration, 21, 22, metadata))
{
Assert.Equal(21, image.Width);
Assert.Equal(22, image.Height);
Assert.Same(configuration, image.GetConfiguration());
Assert.Same(metadata, image.MetaData);
Assert.Equal(dirtyValue, image[5, 5].PackedValue);
}
}
}
}
}

58
tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs

@ -0,0 +1,58 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization
{
public class OctreeQuantizerTests
{
[Fact]
public void OctreeQuantizerConstructor()
{
var quantizer = new OctreeQuantizer(128);
Assert.Equal(128, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser);
quantizer = new OctreeQuantizer(false);
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors);
Assert.Null(quantizer.Diffuser);
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson);
Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson, 128);
Assert.Equal(128, quantizer.MaxColors);
Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser);
}
[Fact]
public void OctreeQuantizerCanCreateFrameQuantizer()
{
var quantizer = new OctreeQuantizer();
IFrameQuantizer<Rgba32> frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser);
quantizer = new OctreeQuantizer(false);
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.False(frameQuantizer.Dither);
Assert.Null(frameQuantizer.Diffuser);
quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson);
frameQuantizer = quantizer.CreateFrameQuantizer<Rgba32>(Configuration.Default);
Assert.NotNull(frameQuantizer);
Assert.True(frameQuantizer.Dither);
Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser);
}
}
}

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

Loading…
Cancel
Save