Browse Source

Merge pull request #487 from SixLabors/js/non-generic-iquantizer

Use non-generic IQuantizer for image formats
pull/492/head
James Jackson-South 8 years ago
committed by GitHub
parent
commit
bfa854939d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/ImageSharp/Common/Helpers/Guard.cs
  2. 15
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  3. 49
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  4. 10
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  5. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  6. 1
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  7. 10
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  8. 15
      src/ImageSharp/Formats/Png/PngEncoder.cs
  9. 148
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  10. 1
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  11. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  12. 1
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  13. 1
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  14. 2
      src/ImageSharp/Primitives/LongRational.cs
  15. 2
      src/ImageSharp/Primitives/Rational.cs
  16. 2
      src/ImageSharp/Primitives/SignedRational.cs
  17. 4
      src/ImageSharp/Processing/Dithering/DiffuseExtensions.cs
  18. 2
      src/ImageSharp/Processing/Dithering/DitherExtensions.cs
  19. 2
      src/ImageSharp/Processing/Dithering/KnownDiffusers.cs
  20. 2
      src/ImageSharp/Processing/Dithering/KnownDitherers.cs
  21. 24
      src/ImageSharp/Processing/Filters/ColorBlindnessExtensions.cs
  22. 2
      src/ImageSharp/Processing/Filters/ColorBlindnessMode.cs
  23. 2
      src/ImageSharp/Processing/Filters/KnownFilterMatrices.cs
  24. 2
      src/ImageSharp/Processing/Filters/Processors/AchromatomalyProcessor.cs
  25. 2
      src/ImageSharp/Processing/Filters/Processors/AchromatopsiaProcessor.cs
  26. 2
      src/ImageSharp/Processing/Filters/Processors/BlackWhiteProcessor.cs
  27. 2
      src/ImageSharp/Processing/Filters/Processors/BrightnessProcessor.cs
  28. 2
      src/ImageSharp/Processing/Filters/Processors/ContrastProcessor.cs
  29. 2
      src/ImageSharp/Processing/Filters/Processors/DeuteranomalyProcessor.cs
  30. 2
      src/ImageSharp/Processing/Filters/Processors/DeuteranopiaProcessor.cs
  31. 2
      src/ImageSharp/Processing/Filters/Processors/GrayscaleBt601Processor.cs
  32. 2
      src/ImageSharp/Processing/Filters/Processors/GrayscaleBt709Processor.cs
  33. 2
      src/ImageSharp/Processing/Filters/Processors/HueProcessor.cs
  34. 2
      src/ImageSharp/Processing/Filters/Processors/InvertProcessor.cs
  35. 2
      src/ImageSharp/Processing/Filters/Processors/KodachromeProcessor.cs
  36. 2
      src/ImageSharp/Processing/Filters/Processors/LomographProcessor.cs
  37. 2
      src/ImageSharp/Processing/Filters/Processors/OpacityProcessor.cs
  38. 2
      src/ImageSharp/Processing/Filters/Processors/PolaroidProcessor.cs
  39. 2
      src/ImageSharp/Processing/Filters/Processors/ProtanomalyProcessor.cs
  40. 2
      src/ImageSharp/Processing/Filters/Processors/ProtanopiaProcessor.cs
  41. 2
      src/ImageSharp/Processing/Filters/Processors/SaturateProcessor.cs
  42. 2
      src/ImageSharp/Processing/Filters/Processors/SepiaProcessor.cs
  43. 2
      src/ImageSharp/Processing/Filters/Processors/TritanomalyProcessor.cs
  44. 2
      src/ImageSharp/Processing/Filters/Processors/TritanopiaProcessor.cs
  45. 56
      src/ImageSharp/Processing/Quantization/Box.cs
  46. 27
      src/ImageSharp/Processing/Quantization/FrameQuantizers/FrameQuantizerBase{TPixel}.cs
  47. 35
      src/ImageSharp/Processing/Quantization/FrameQuantizers/IFrameQuantizer{TPixel}.cs
  48. 34
      src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs
  49. 36
      src/ImageSharp/Processing/Quantization/FrameQuantizers/PaletteFrameQuantizer{TPixel}.cs
  50. 76
      src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs
  51. 28
      src/ImageSharp/Processing/Quantization/IQuantizer.cs
  52. 42
      src/ImageSharp/Processing/Quantization/IQuantizer{TPixel}.cs
  53. 29
      src/ImageSharp/Processing/Quantization/KnownQuantizers.cs
  54. 83
      src/ImageSharp/Processing/Quantization/OctreeQuantizer.cs
  55. 65
      src/ImageSharp/Processing/Quantization/PaletteQuantizer.cs
  56. 53
      src/ImageSharp/Processing/Quantization/Processors/QuantizeProcessor.cs
  57. 29
      src/ImageSharp/Processing/Quantization/QuantizationMode.cs
  58. 31
      src/ImageSharp/Processing/Quantization/QuantizeExtensions.cs
  59. 82
      src/ImageSharp/Processing/Quantization/WuQuantizer.cs
  60. 2
      src/ImageSharp/Processing/Transforms/AnchorPositionMode.cs
  61. 6
      src/ImageSharp/Processing/Transforms/FlipExtensions.cs
  62. 2
      src/ImageSharp/Processing/Transforms/FlipMode.cs
  63. 2
      src/ImageSharp/Processing/Transforms/KnownResamplers.cs
  64. 2
      src/ImageSharp/Processing/Transforms/OrientationMode.cs
  65. 2
      src/ImageSharp/Processing/Transforms/PadExtensions.cs
  66. 54
      src/ImageSharp/Processing/Transforms/Processors/AutoOrientProcessor.cs
  67. 16
      src/ImageSharp/Processing/Transforms/Processors/FlipProcessor.cs
  68. 2
      src/ImageSharp/Processing/Transforms/Processors/RotateProcessor.cs
  69. 2
      src/ImageSharp/Processing/Transforms/Processors/SkewProcessor.cs
  70. 8
      src/ImageSharp/Processing/Transforms/ResizeExtensions.cs
  71. 64
      src/ImageSharp/Processing/Transforms/ResizeHelper.cs
  72. 2
      src/ImageSharp/Processing/Transforms/ResizeMode.cs
  73. 4
      src/ImageSharp/Processing/Transforms/ResizeOptions.cs
  74. 8
      src/ImageSharp/Processing/Transforms/RotateExtensions.cs
  75. 8
      src/ImageSharp/Processing/Transforms/RotateFlipExtensions.cs
  76. 2
      src/ImageSharp/Processing/Transforms/RotateMode.cs
  77. 2
      src/ImageSharp/Processing/Transforms/SkewExtensions.cs
  78. 4
      src/ImageSharp/Processing/Transforms/TransformExtensions.cs
  79. 53
      tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs
  80. 10
      tests/ImageSharp.Benchmarks/Image/EncodePng.cs
  81. 2
      tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
  82. 6
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  83. 64
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  84. 2
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
  85. 2
      tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs
  86. 2
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
  87. 2
      tests/ImageSharp.Tests/Numerics/RationalTests.cs
  88. 2
      tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs
  89. 4
      tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs
  90. 4
      tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs
  91. 20
      tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs
  92. 4
      tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs
  93. 30
      tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs
  94. 30
      tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs
  95. 22
      tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs
  96. 6
      tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs
  97. 24
      tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs
  98. 12
      tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs
  99. 2
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs
  100. 38
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

10
src/ImageSharp/Common/Helpers/Guard.cs

@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp
{
if (value.CompareTo(max) >= 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}.");
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}.");
}
}
@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp
{
if (value.CompareTo(max) > 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}.");
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}.");
}
}
@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp
{
throw new ArgumentOutOfRangeException(
parameterName,
$"Value must be greater than {min}.");
$"Value {value} must be greater than {min}.");
}
}
@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp
{
if (value.CompareTo(min) < 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}.");
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}.");
}
}
@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp
{
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min} and less than or equal to {max}.");
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}.");
}
}

15
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -24,20 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Gets or sets the size of the color palette to use. For gifs the value ranges from 1 to 256. Leave as zero for default size.
/// </summary>
public int PaletteSize { get; set; } = 0;
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 128;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// Defaults to the <see cref="OctreeQuantizer"/>
/// </summary>
public IQuantizer Quantizer { get; set; }
public IQuantizer Quantizer { get; set; } = new OctreeQuantizer();
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
@ -47,4 +38,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
encoder.Encode(image, stream);
}
}
}
}

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

@ -25,40 +25,30 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private readonly byte[] buffer = new byte[16];
/// <summary>
/// The number of bits requires to store the image palette.
/// </summary>
private int bitDepth;
/// <summary>
/// Whether the current image has multiple frames.
/// </summary>
private bool hasFrames;
/// <summary>
/// Gets the TextEncoding
/// </summary>
private Encoding textEncoding;
private readonly Encoding textEncoding;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
private IQuantizer quantizer;
private readonly IQuantizer quantizer;
/// <summary>
/// Gets or sets the threshold.
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private byte threshold;
private readonly bool ignoreMetadata;
/// <summary>
/// Gets or sets the size of the color palette to use.
/// The number of bits requires to store the image palette.
/// </summary>
private int paletteSize;
private int bitDepth;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// Whether the current image has multiple frames.
/// </summary>
private bool ignoreMetadata;
private bool hasFrames;
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
@ -69,10 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
this.memoryManager = memoryManager;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
this.threshold = options.Threshold;
this.paletteSize = options.PaletteSize;
this.ignoreMetadata = options.IgnoreMetadata;
}
@ -88,24 +75,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.quantizer = this.quantizer ?? new OctreeQuantizer<TPixel>();
// Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
// Ensure that pallete size can be set but has a fallback.
int size = this.paletteSize;
size = size > 0 ? size.Clamp(1, 256) : 256;
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(size);
this.hasFrames = image.Frames.Count > 1;
var pixelQuantizer = (IQuantizer<TPixel>)this.quantizer;
// Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = pixelQuantizer.Quantize(image.Frames.RootFrame, size);
QuantizedFrame<TPixel> quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
int index = this.GetTransparentIndex(quantized);
@ -128,7 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
if (quantized == null)
{
quantized = pixelQuantizer.Quantize(frame, size);
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(frame);
}
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantized));
@ -136,7 +115,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.WriteColorTable(quantized, writer);
this.WriteImageData(quantized, writer);
quantized = null; // so next frame can regenerate it
quantized = null; // So next frame can regenerate it
}
// TODO: Write extension etc

10
src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs

@ -21,16 +21,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
Encoding TextEncoding { get; }
/// <summary>
/// Gets the size of the color palette to use. For gifs the value ranges from 1 to 256. Leave as zero for default size.
/// </summary>
int PaletteSize { get; }
/// <summary>
/// Gets the transparency threshold.
/// </summary>
byte Threshold { get; }
/// <summary>
/// Gets the quantizer for reducing the color count.
/// </summary>

2
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder;
@ -11,6 +10,7 @@ using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort

1
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -12,6 +12,7 @@ using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{

10
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -10,16 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal interface IPngEncoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the size of the color palette to use. Set to zero to leav png encoding to use pixel data.
/// </summary>
int PaletteSize { get; }
/// <summary>
/// Gets the png color type
/// </summary>

15
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -13,16 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <summary>
/// Gets or sets the size of the color palette to use. Set to zero to leave png encoding to use pixel data.
/// </summary>
public int PaletteSize { get; set; } = 0;
/// <summary>
/// Gets or sets the png color type
/// </summary>
@ -44,8 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Gets or sets quantizer for reducing the color count.
/// Defaults to the <see cref="WuQuantizer"/>
/// </summary>
public IQuantizer Quantizer { get; set; }
public IQuantizer Quantizer { get; set; } = new WuQuantizer();
/// <summary>
/// Gets or sets the transparency threshold.
@ -73,4 +64,4 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
}
}
}

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

@ -40,6 +40,36 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The png color type.
/// </summary>
private readonly PngColorType pngColorType;
/// <summary>
/// The quantizer for reducing the color count.
/// </summary>
private readonly IQuantizer quantizer;
/// <summary>
/// Gets or sets the CompressionLevel value
/// </summary>
private readonly int compressionLevel;
/// <summary>
/// Gets or sets the Gamma value
/// </summary>
private readonly float gamma;
/// <summary>
/// Gets or sets the Threshold value
/// </summary>
private readonly byte threshold;
/// <summary>
/// Gets or sets a value indicating whether to Write Gamma
/// </summary>
private readonly bool writeGamma;
/// <summary>
/// Contains the raw pixel data from an indexed image.
/// </summary>
@ -101,50 +131,10 @@ namespace SixLabors.ImageSharp.Formats.Png
private IManagedByteBuffer average;
/// <summary>
/// The buffer for the paeth filter
/// The buffer for the Paeth filter
/// </summary>
private IManagedByteBuffer paeth;
/// <summary>
/// The png color type.
/// </summary>
private PngColorType pngColorType;
/// <summary>
/// The quantizer for reducing the color count.
/// </summary>
private IQuantizer quantizer;
/// <summary>
/// Gets or sets a value indicating whether to ignore metadata
/// </summary>
private bool ignoreMetadata;
/// <summary>
/// Gets or sets the Quality value
/// </summary>
private int paletteSize;
/// <summary>
/// Gets or sets the CompressionLevel value
/// </summary>
private int compressionLevel;
/// <summary>
/// Gets or sets the Gamma value
/// </summary>
private float gamma;
/// <summary>
/// Gets or sets the Threshold value
/// </summary>
private byte threshold;
/// <summary>
/// Gets or sets a value indicating whether to Write Gamma
/// </summary>
private bool writeGamma;
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderCore"/> class.
/// </summary>
@ -153,8 +143,6 @@ namespace SixLabors.ImageSharp.Formats.Png
public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options)
{
this.memoryManager = memoryManager;
this.ignoreMetadata = options.IgnoreMetadata;
this.paletteSize = options.PaletteSize > 0 ? options.PaletteSize.Clamp(1, int.MaxValue) : int.MaxValue;
this.pngColorType = options.PngColorType;
this.compressionLevel = options.CompressionLevel;
this.gamma = options.Gamma;
@ -190,28 +178,27 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(this.chunkDataBuffer, 0, 8);
// Set correct color type if the color count is 256 or less.
if (this.paletteSize <= 256)
{
this.pngColorType = PngColorType.Palette;
}
if (this.pngColorType == PngColorType.Palette && this.paletteSize > 256)
QuantizedFrame<TPixel> quantized = null;
if (this.pngColorType == PngColorType.Palette)
{
this.paletteSize = 256;
}
// Create quantized frame returning the palette and set the bit depth.
quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
this.palettePixelData = quantized.Pixels;
byte bits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
// Set correct bit depth.
this.bitDepth = this.paletteSize <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.paletteSize).Clamp(1, 8)
: (byte)8;
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
if (bits == 3)
{
bits = 4;
}
else if (bits >= 5 || bits <= 7)
{
bits = 8;
}
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
if (this.bitDepth == 3)
{
this.bitDepth = 4;
this.bitDepth = bits;
}
else if (this.bitDepth >= 5 || this.bitDepth <= 7)
else
{
this.bitDepth = 8;
}
@ -232,9 +219,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WriteHeaderChunk(stream, header);
// Collect the indexed pixel data
if (this.pngColorType == PngColorType.Palette)
if (quantized != null)
{
this.CollectIndexedBytes(image.Frames.RootFrame, stream, header);
this.WritePaletteChunk(stream, header, quantized);
}
this.WritePhysicalChunk(stream, image);
@ -296,21 +283,6 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(buffer, 0, 4);
}
/// <summary>
/// Collects the indexed pixel data.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to encode.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
private void CollectIndexedBytes<TPixel>(ImageFrame<TPixel> image, Stream stream, PngHeader header)
where TPixel : struct, IPixel<TPixel>
{
// Quantize the image and get the pixels.
QuantizedFrame<TPixel> quantized = this.WritePaletteChunk(stream, header, image);
this.palettePixelData = quantized.Pixels;
}
/// <summary>
/// Collects a row of grayscale pixels.
/// </summary>
@ -496,24 +468,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
/// <param name="image">The image to encode.</param>
/// <returns>The <see cref="QuantizedFrame{TPixel}"/></returns>
private QuantizedFrame<TPixel> WritePaletteChunk<TPixel>(Stream stream, PngHeader header, ImageFrame<TPixel> image)
/// <param name="quantized">The quantized frame.</param>
private void WritePaletteChunk<TPixel>(Stream stream, PngHeader header, QuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
if (this.paletteSize > 256)
{
return null;
}
if (this.quantizer == null)
{
this.quantizer = new WuQuantizer<TPixel>();
}
// Quantize the image returning a palette. This boxing is icky.
QuantizedFrame<TPixel> quantized = ((IQuantizer<TPixel>)this.quantizer).Quantize(image, this.paletteSize);
// Grab the palette and write it to the stream.
TPixel[] palette = quantized.Palette;
byte pixelCount = palette.Length.ToByte();
@ -560,8 +518,6 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, alphaTable.Array, 0, pixelCount);
}
}
return quantized;
}
/// <summary>

1
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{

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

@ -1,10 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{

1
src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs

@ -4,6 +4,7 @@
using System;
using System.Globalization;
using System.Text;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{

1
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{

2
src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs → src/ImageSharp/Primitives/LongRational.cs

@ -5,7 +5,7 @@ using System;
using System.Globalization;
using System.Text;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
namespace SixLabors.ImageSharp.Primitives
{
/// <summary>
/// Represents a number that can be expressed as a fraction

2
src/ImageSharp/MetaData/Profiles/Exif/Rational.cs → src/ImageSharp/Primitives/Rational.cs

@ -4,7 +4,7 @@
using System;
using System.Globalization;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
namespace SixLabors.ImageSharp.Primitives
{
/// <summary>
/// Represents a number that can be expressed as a fraction.

2
src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs → src/ImageSharp/Primitives/SignedRational.cs

@ -4,7 +4,7 @@
using System;
using System.Globalization;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
namespace SixLabors.ImageSharp.Primitives
{
/// <summary>
/// Represents a number that can be expressed as a fraction.

4
src/ImageSharp/Processing/Dithering/DiffuseExtensions.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Diffuse<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> Diffuse(source, DiffuseMode.FloydSteinberg, .5F);
=> Diffuse(source, KnownDiffusers.FloydSteinberg, .5F);
/// <summary>
/// Dithers the image reducing it to a web-safe palette using error diffusion.
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Diffuse<TPixel>(this IImageProcessingContext<TPixel> source, float threshold)
where TPixel : struct, IPixel<TPixel>
=> Diffuse(source, DiffuseMode.FloydSteinberg, threshold);
=> Diffuse(source, KnownDiffusers.FloydSteinberg, threshold);
/// <summary>
/// Dithers the image reducing it to a web-safe palette using error diffusion.

2
src/ImageSharp/Processing/Dithering/DitherExtensions.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Dither<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> Dither(source, DitherMode.BayerDither4x4);
=> Dither(source, KnownDitherers.BayerDither4x4);
/// <summary>
/// Dithers the image reducing it to a web-safe palette using ordered dithering.

2
src/ImageSharp/Processing/Dithering/DiffuseMode.cs → src/ImageSharp/Processing/Dithering/KnownDiffusers.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering
/// <summary>
/// Contains reusable static instances of known error diffusion algorithms
/// </summary>
public static class DiffuseMode
public static class KnownDiffusers
{
/// <summary>
/// Gets the error diffuser that implements the Atkinson algorithm.

2
src/ImageSharp/Processing/Dithering/DitherMode.cs → src/ImageSharp/Processing/Dithering/KnownDitherers.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering
/// <summary>
/// Contains reusable static instances of known ordered dither matrices
/// </summary>
public class DitherMode
public class KnownDitherers
{
/// <summary>
/// Gets the order ditherer using the 2x2 Bayer dithering matrix

24
src/ImageSharp/Processing/Filters/ColorBlindnessExtensions.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Filters
/// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> ColorBlindness<TPixel>(this IImageProcessingContext<TPixel> source, ColorBlindness colorBlindness)
public static IImageProcessingContext<TPixel> ColorBlindness<TPixel>(this IImageProcessingContext<TPixel> source, ColorBlindnessMode colorBlindness)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(GetProcessor<TPixel>(colorBlindness));
@ -29,33 +29,33 @@ namespace SixLabors.ImageSharp.Processing.Filters
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="colorBlindness">The type of color blindness simulator to apply.</param>
/// <param name="colorBlindnessMode">The type of color blindness simulator to apply.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> ColorBlindness<TPixel>(this IImageProcessingContext<TPixel> source, ColorBlindness colorBlindness, Rectangle rectangle)
public static IImageProcessingContext<TPixel> ColorBlindness<TPixel>(this IImageProcessingContext<TPixel> source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(GetProcessor<TPixel>(colorBlindness), rectangle);
=> source.ApplyProcessor(GetProcessor<TPixel>(colorBlindnessMode), rectangle);
private static IImageProcessor<TPixel> GetProcessor<TPixel>(ColorBlindness colorBlindness)
private static IImageProcessor<TPixel> GetProcessor<TPixel>(ColorBlindnessMode colorBlindness)
where TPixel : struct, IPixel<TPixel>
{
switch (colorBlindness)
{
case Filters.ColorBlindness.Achromatomaly:
case ColorBlindnessMode.Achromatomaly:
return new AchromatomalyProcessor<TPixel>();
case Filters.ColorBlindness.Achromatopsia:
case ColorBlindnessMode.Achromatopsia:
return new AchromatopsiaProcessor<TPixel>();
case Filters.ColorBlindness.Deuteranomaly:
case ColorBlindnessMode.Deuteranomaly:
return new DeuteranomalyProcessor<TPixel>();
case Filters.ColorBlindness.Deuteranopia:
case ColorBlindnessMode.Deuteranopia:
return new DeuteranopiaProcessor<TPixel>();
case Filters.ColorBlindness.Protanomaly:
case ColorBlindnessMode.Protanomaly:
return new ProtanomalyProcessor<TPixel>();
case Filters.ColorBlindness.Protanopia:
case ColorBlindnessMode.Protanopia:
return new ProtanopiaProcessor<TPixel>();
case Filters.ColorBlindness.Tritanomaly:
case ColorBlindnessMode.Tritanomaly:
return new TritanomalyProcessor<TPixel>();
default:
return new TritanopiaProcessor<TPixel>();

2
src/ImageSharp/Processing/Filters/ColorBlindness.cs → src/ImageSharp/Processing/Filters/ColorBlindnessMode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Filters
/// <summary>
/// Enumerates the various types of defined color blindness filters.
/// </summary>
public enum ColorBlindness
public enum ColorBlindnessMode
{
/// <summary>
/// Partial color desensitivity.

2
src/ImageSharp/Processing/Filters/MatrixFilters.cs → src/ImageSharp/Processing/Filters/KnownFilterMatrices.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Filters
/// <summary>
/// A collection of known <see cref="Matrix4x4"/> values for composing filters
/// </summary>
public static class MatrixFilters
public static class KnownFilterMatrices
{
/// <summary>
/// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness

2
src/ImageSharp/Processing/Filters/Processors/AchromatomalyProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="AchromatomalyProcessor{TPixel}"/> class.
/// </summary>
public AchromatomalyProcessor()
: base(MatrixFilters.AchromatomalyFilter)
: base(KnownFilterMatrices.AchromatomalyFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/AchromatopsiaProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="AchromatopsiaProcessor{TPixel}"/> class.
/// </summary>
public AchromatopsiaProcessor()
: base(MatrixFilters.AchromatopsiaFilter)
: base(KnownFilterMatrices.AchromatopsiaFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/BlackWhiteProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="BlackWhiteProcessor{TPixel}"/> class.
/// </summary>
public BlackWhiteProcessor()
: base(MatrixFilters.BlackWhiteFilter)
: base(KnownFilterMatrices.BlackWhiteFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/BrightnessProcessor.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
public BrightnessProcessor(float amount)
: base(MatrixFilters.CreateBrightnessFilter(amount))
: base(KnownFilterMatrices.CreateBrightnessFilter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/ContrastProcessor.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
public ContrastProcessor(float amount)
: base(MatrixFilters.CreateContrastFilter(amount))
: base(KnownFilterMatrices.CreateContrastFilter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/DeuteranomalyProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="DeuteranomalyProcessor{TPixel}"/> class.
/// </summary>
public DeuteranomalyProcessor()
: base(MatrixFilters.DeuteranomalyFilter)
: base(KnownFilterMatrices.DeuteranomalyFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/DeuteranopiaProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="DeuteranopiaProcessor{TPixel}"/> class.
/// </summary>
public DeuteranopiaProcessor()
: base(MatrixFilters.DeuteranopiaFilter)
: base(KnownFilterMatrices.DeuteranopiaFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/GrayscaleBt601Processor.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public GrayscaleBt601Processor(float amount)
: base(MatrixFilters.CreateGrayscaleBt601Filter(amount))
: base(KnownFilterMatrices.CreateGrayscaleBt601Filter(amount))
{
this.Amount = amount;
}

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

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public GrayscaleBt709Processor(float amount)
: base(MatrixFilters.CreateGrayscaleBt709Filter(amount))
: base(KnownFilterMatrices.CreateGrayscaleBt709Filter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/HueProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </summary>
/// <param name="degrees">The angle of rotation in degrees</param>
public HueProcessor(float degrees)
: base(MatrixFilters.CreateHueFilter(degrees))
: base(KnownFilterMatrices.CreateHueFilter(degrees))
{
this.Degrees = degrees;
}

2
src/ImageSharp/Processing/Filters/Processors/InvertProcessor.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public InvertProcessor(float amount)
: base(MatrixFilters.CreateInvertFilter(amount))
: base(KnownFilterMatrices.CreateInvertFilter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/KodachromeProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="KodachromeProcessor{TPixel}"/> class.
/// </summary>
public KodachromeProcessor()
: base(MatrixFilters.KodachromeFilter)
: base(KnownFilterMatrices.KodachromeFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/LomographProcessor.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="LomographProcessor{TPixel}" /> class.
/// </summary>
public LomographProcessor()
: base(MatrixFilters.LomographFilter)
: base(KnownFilterMatrices.LomographFilter)
{
}

2
src/ImageSharp/Processing/Filters/Processors/OpacityProcessor.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public OpacityProcessor(float amount)
: base(MatrixFilters.CreateOpacityFilter(amount))
: base(KnownFilterMatrices.CreateOpacityFilter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/PolaroidProcessor.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="PolaroidProcessor{TPixel}" /> class.
/// </summary>
public PolaroidProcessor()
: base(MatrixFilters.PolaroidFilter)
: base(KnownFilterMatrices.PolaroidFilter)
{
}

2
src/ImageSharp/Processing/Filters/Processors/ProtanomalyProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="ProtanomalyProcessor{TPixel}"/> class.
/// </summary>
public ProtanomalyProcessor()
: base(MatrixFilters.ProtanomalyFilter)
: base(KnownFilterMatrices.ProtanomalyFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/ProtanopiaProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="ProtanopiaProcessor{TPixel}"/> class.
/// </summary>
public ProtanopiaProcessor()
: base(MatrixFilters.ProtanopiaFilter)
: base(KnownFilterMatrices.ProtanopiaFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/SaturateProcessor.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </remarks>
/// <param name="amount">The proportion of the conversion. Must be greater than or equal to 0.</param>
public SaturateProcessor(float amount)
: base(MatrixFilters.CreateSaturateFilter(amount))
: base(KnownFilterMatrices.CreateSaturateFilter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/SepiaProcessor.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// </summary>
/// <param name="amount">The proportion of the conversion. Must be between 0 and 1.</param>
public SepiaProcessor(float amount)
: base(MatrixFilters.CreateSepiaFilter(amount))
: base(KnownFilterMatrices.CreateSepiaFilter(amount))
{
this.Amount = amount;
}

2
src/ImageSharp/Processing/Filters/Processors/TritanomalyProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="TritanomalyProcessor{TPixel}"/> class.
/// </summary>
public TritanomalyProcessor()
: base(MatrixFilters.TritanomalyFilter)
: base(KnownFilterMatrices.TritanomalyFilter)
{
}
}

2
src/ImageSharp/Processing/Filters/Processors/TritanopiaProcessor.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Filters.Processors
/// Initializes a new instance of the <see cref="TritanopiaProcessor{TPixel}"/> class.
/// </summary>
public TritanopiaProcessor()
: base(MatrixFilters.TritanopiaFilter)
: base(KnownFilterMatrices.TritanopiaFilter)
{
}
}

56
src/ImageSharp/Processing/Quantization/Box.cs

@ -1,56 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Represents a box color cube.
/// </summary>
internal struct Box
{
/// <summary>
/// Gets or sets the min red value, exclusive.
/// </summary>
public int R0 { get; set; }
/// <summary>
/// Gets or sets the max red value, inclusive.
/// </summary>
public int R1 { get; set; }
/// <summary>
/// Gets or sets the min green value, exclusive.
/// </summary>
public int G0 { get; set; }
/// <summary>
/// Gets or sets the max green value, inclusive.
/// </summary>
public int G1 { get; set; }
/// <summary>
/// Gets or sets the min blue value, exclusive.
/// </summary>
public int B0 { get; set; }
/// <summary>
/// Gets or sets the max blue value, inclusive.
/// </summary>
public int B1 { get; set; }
/// <summary>
/// Gets or sets the min alpha value, exclusive.
/// </summary>
public int A0 { get; set; }
/// <summary>
/// Gets or sets the max alpha value, inclusive.
/// </summary>
public int A1 { get; set; }
/// <summary>
/// Gets or sets the volume.
/// </summary>
public int Volume { get; set; }
}
}

27
src/ImageSharp/Processing/Quantization/QuantizerBase{TPixel}.cs → src/ImageSharp/Processing/Quantization/FrameQuantizers/FrameQuantizerBase{TPixel}.cs

@ -6,16 +6,15 @@ using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
namespace SixLabors.ImageSharp.Processing.Quantization
namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{
/// <summary>
/// Encapsulates methods to calculate the color palette of an image.
/// The base class for all <see cref="IFrameQuantizer{TPixel}"/> implementations
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public abstract class QuantizerBase<TPixel> : IQuantizer<TPixel>
public abstract class FrameQuantizerBase<TPixel> : IFrameQuantizer<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -24,29 +23,35 @@ namespace SixLabors.ImageSharp.Processing.Quantization
private readonly bool singlePass;
/// <summary>
/// Initializes a new instance of the <see cref="QuantizerBase{TPixel}"/> class.
/// Initializes a new instance of the <see cref="FrameQuantizerBase{TPixel}"/> class.
/// </summary>
/// <param name="quantizer">The quantizer</param>
/// <param name="singlePass">
/// If true, the quantization only needs to loop through the source pixels once
/// If true, the quantization process only needs to loop through the source pixels once
/// </param>
/// <remarks>
/// If you construct this class with a true value for singlePass, then the code will, when quantizing your image,
/// only call the 'QuantizeImage' function. If two passes are required, the code will call 'InitialQuantizeImage'
/// only call the <see cref="FirstPass(ImageFrame{TPixel}, int, int)"/> methods.
/// If two passes are required, the code will also call <see cref="SecondPass(ImageFrame{TPixel}, byte[], int, int)"/>
/// and then 'QuantizeImage'.
/// </remarks>
protected QuantizerBase(bool singlePass)
protected FrameQuantizerBase(IQuantizer quantizer, bool singlePass)
{
Guard.NotNull(quantizer, nameof(quantizer));
this.Diffuser = quantizer.Diffuser;
this.Dither = this.Diffuser != null;
this.singlePass = singlePass;
}
/// <inheritdoc />
public bool Dither { get; set; } = true;
public bool Dither { get; }
/// <inheritdoc />
public IErrorDiffuser DitherType { get; set; } = DiffuseMode.FloydSteinberg;
public IErrorDiffuser Diffuser { get; }
/// <inheritdoc/>
public virtual QuantizedFrame<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
public virtual QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image)
{
Guard.NotNull(image, nameof(image));

35
src/ImageSharp/Processing/Quantization/FrameQuantizers/IFrameQuantizer{TPixel}.cs

@ -0,0 +1,35 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{
/// <summary>
/// Provides methods to allow the execution of the quantization process on an image frame.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IFrameQuantizer<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets a value indicating whether to apply dithering to the output image.
/// </summary>
bool Dither { get; }
/// <summary>
/// Gets the error diffusion algorithm to apply to the output image.
/// </summary>
IErrorDiffuser Diffuser { get; }
/// <summary>
/// Quantize an image frame and return the resulting output pixels.
/// </summary>
/// <param name="image">The image to quantize.</param>
/// <returns>
/// A <see cref="QuantizedFrame{TPixel}"/> representing a quantized version of the image pixels.
/// </returns>
QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image);
}
}

34
src/ImageSharp/Processing/Quantization/OctreeQuantizer{TPixel}.cs → src/ImageSharp/Processing/Quantization/FrameQuantizers/OctreeFrameQuantizer{TPixel}.cs

@ -8,14 +8,14 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Quantization
namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{
/// <summary>
/// Encapsulates methods to calculate the color palette if an image using an Octree pattern.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class OctreeQuantizer<TPixel> : QuantizerBase<TPixel>
internal sealed class OctreeFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -24,14 +24,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization
private readonly Dictionary<TPixel, byte> colorMap = new Dictionary<TPixel, byte>();
/// <summary>
/// Stores the tree
/// Maximum allowed color depth
/// </summary>
private Octree octree;
private readonly byte colors;
/// <summary>
/// Maximum allowed color depth
/// Stores the tree
/// </summary>
private byte colors;
private readonly Octree octree;
/// <summary>
/// The reduced image palette
@ -44,26 +44,18 @@ namespace SixLabors.ImageSharp.Processing.Quantization
private byte transparentIndex;
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer{TPixel}"/> class.
/// Initializes a new instance of the <see cref="OctreeFrameQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="quantizer">The octree quantizer</param>
/// <remarks>
/// The Octree quantizer is a two pass algorithm. The initial pass sets up the Octree,
/// the second pass quantizes a color based on the nodes in the tree
/// </remarks>
public OctreeQuantizer()
: base(false)
public OctreeFrameQuantizer(OctreeQuantizer quantizer)
: base(quantizer, false)
{
}
/// <inheritdoc/>
public override QuantizedFrame<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{
this.colors = (byte)maxColors.Clamp(1, 255);
this.colors = (byte)quantizer.MaxColors;
this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors));
this.palette = null;
this.colorMap.Clear();
return base.Quantize(image, this.colors);
}
/// <inheritdoc/>
@ -129,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization
if (this.Dither)
{
// Apply the dithering matrix. We have to reapply the value now as the original has changed.
this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
}
output[(y * source.Width) + x] = pixelValue;
@ -322,7 +314,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization
}
// Now palletize the nodes
TPixel[] palette = new TPixel[colorCount + 1];
var palette = new TPixel[colorCount + 1];
int paletteIndex = 0;
this.root.ConstructPalette(palette, ref paletteIndex);

36
src/ImageSharp/Processing/Quantization/PaletteQuantizer{TPixel}.cs → src/ImageSharp/Processing/Quantization/FrameQuantizers/PaletteFrameQuantizer{TPixel}.cs

@ -7,15 +7,14 @@ using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Quantization
namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{
/// <summary>
/// Encapsulates methods to create a quantized image based upon the given palette.
/// If no palette is given this will default to the web safe colors defined in the CSS Color Module Level 4.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class PaletteQuantizer<TPixel> : QuantizerBase<TPixel>
internal sealed class PaletteFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
@ -26,33 +25,16 @@ namespace SixLabors.ImageSharp.Processing.Quantization
/// <summary>
/// List of all colors in the palette
/// </summary>
private TPixel[] colors;
private readonly TPixel[] colors;
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class.
/// </summary>
public PaletteQuantizer()
: this(NamedColors<TPixel>.WebSafePalette)
/// <param name="quantizer">The palette quantizer</param>
public PaletteFrameQuantizer(PaletteQuantizer quantizer)
: base(quantizer, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="palette">The palette to select substitute colors from.</param>
public PaletteQuantizer(TPixel[] palette = null)
: base(true)
{
Guard.NotNull(palette, nameof(palette));
this.colors = palette;
}
/// <inheritdoc/>
public override QuantizedFrame<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{
Array.Resize(ref this.colors, maxColors.Clamp(1, 255));
this.colorMap.Clear();
return base.Quantize(image, maxColors);
this.colors = quantizer.GetPalette<TPixel>();
}
/// <inheritdoc/>
@ -95,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization
if (this.Dither)
{
// Apply the dithering matrix. We have to reapply the value now as the original has changed.
this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
}
output[(y * source.Width) + x] = pixelValue;

76
src/ImageSharp/Processing/Quantization/WuQuantizer{TPixel}.cs → src/ImageSharp/Processing/Quantization/FrameQuantizers/WuFrameQuantizer{TPixel}.cs

@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Quantization
namespace SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers
{
/// <summary>
/// An implementation of Wu's color quantizer with alpha channel.
@ -32,10 +32,10 @@ namespace SixLabors.ImageSharp.Processing.Quantization
/// </para>
/// </remarks>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public class WuQuantizer<TPixel> : QuantizerBase<TPixel>
internal sealed class WuFrameQuantizer<TPixel> : FrameQuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel>
{
// TODO: The WuQuantizer<TPixel> code is rising several questions:
// TODO: The WuFrameQuantizer<TPixel> code is rising several questions:
// - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes )
// - Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case?
// (T, R, G, B, A, M2) could be grouped together!
@ -124,26 +124,23 @@ namespace SixLabors.ImageSharp.Processing.Quantization
private Box[] colorCube;
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer{TPixel}"/> class.
/// Initializes a new instance of the <see cref="WuFrameQuantizer{TPixel}"/> class.
/// </summary>
/// <param name="quantizer">The wu quantizer</param>
/// <remarks>
/// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram,
/// the second pass quantizes a color based on the position in the histogram.
/// </remarks>
public WuQuantizer()
: base(false)
public WuFrameQuantizer(WuQuantizer quantizer)
: base(quantizer, false)
{
this.colors = quantizer.MaxColors;
}
/// <inheritdoc/>
public override QuantizedFrame<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
public override QuantizedFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> image)
{
Guard.NotNull(image, nameof(image));
this.colors = maxColors.Clamp(1, 255);
this.palette = null;
this.colorMap.Clear();
MemoryManager memoryManager = image.MemoryManager;
try
@ -156,7 +153,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization
this.m2 = memoryManager.AllocateClean<float>(TableLength);
this.tag = memoryManager.AllocateClean<byte>(TableLength);
return base.Quantize(image, this.colors);
return base.QuantizeFrame(image);
}
finally
{
@ -293,7 +290,7 @@ namespace SixLabors.ImageSharp.Processing.Quantization
if (this.Dither)
{
// Apply the dithering matrix. We have to reapply the value now as the original has changed.
this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height);
}
output[(y * source.Width) + x] = pixelValue;
@ -873,5 +870,56 @@ namespace SixLabors.ImageSharp.Processing.Quantization
return tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)];
}
/// <summary>
/// Represents a box color cube.
/// </summary>
private struct Box
{
/// <summary>
/// Gets or sets the min red value, exclusive.
/// </summary>
public int R0;
/// <summary>
/// Gets or sets the max red value, inclusive.
/// </summary>
public int R1;
/// <summary>
/// Gets or sets the min green value, exclusive.
/// </summary>
public int G0;
/// <summary>
/// Gets or sets the max green value, inclusive.
/// </summary>
public int G1;
/// <summary>
/// Gets or sets the min blue value, exclusive.
/// </summary>
public int B0;
/// <summary>
/// Gets or sets the max blue value, inclusive.
/// </summary>
public int B1;
/// <summary>
/// Gets or sets the min alpha value, exclusive.
/// </summary>
public int A0;
/// <summary>
/// Gets or sets the max alpha value, inclusive.
/// </summary>
public int A1;
/// <summary>
/// Gets or sets the volume.
/// </summary>
public int Volume;
}
}
}

28
src/ImageSharp/Processing/Quantization/IQuantizer.cs

@ -0,0 +1,28 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
using SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers;
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Provides methods for allowing quantization of images pixels with configurable dithering.
/// </summary>
public interface IQuantizer
{
/// <summary>
/// Gets the error diffusion algorithm to apply to the output image.
/// </summary>
IErrorDiffuser Diffuser { get; }
/// <summary>
/// Creates the generic frame quantizer
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>The <see cref="IFrameQuantizer{TPixel}"/></returns>
IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>;
}
}

42
src/ImageSharp/Processing/Quantization/IQuantizer{TPixel}.cs

@ -1,42 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Provides methods for for allowing quantization of images pixels with configurable dithering.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IQuantizer<TPixel> : IQuantizer
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Quantize an image and return the resulting output pixels.
/// </summary>
/// <param name="image">The image to quantize.</param>
/// <param name="maxColors">The maximum number of colors to return.</param>
/// <returns>
/// A <see cref="T:QuantizedImage"/> representing a quantized version of the image pixels.
/// </returns>
QuantizedFrame<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors);
}
/// <summary>
/// Provides methods for allowing quantization of images pixels with configurable dithering.
/// </summary>
public interface IQuantizer
{
/// <summary>
/// Gets or sets a value indicating whether to apply dithering to the output image.
/// </summary>
bool Dither { get; set; }
/// <summary>
/// Gets or sets the dithering algorithm to apply to the output image.
/// </summary>
IErrorDiffuser DitherType { get; set; }
}
}

29
src/ImageSharp/Processing/Quantization/KnownQuantizers.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Contains reusable static instances of known quantizing algorithms
/// </summary>
public static class KnownQuantizers
{
/// <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.
/// </summary>
public static IQuantizer Palette { get; } = new PaletteQuantizer();
}
}

83
src/ImageSharp/Processing/Quantization/OctreeQuantizer.cs

@ -0,0 +1,83 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
using SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers;
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Allows the quantization of images pixels using Octrees.
/// <see href="http://msdn.microsoft.com/en-us/library/aa479306.aspx"/>
/// <para>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering and a color palette of a maximum length of <value>255</value>
/// </para>
/// </summary>
public class OctreeQuantizer : IQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
public OctreeQuantizer()
: this(true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
/// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
public OctreeQuantizer(int maxColors)
: this(GetDiffuser(true), maxColors)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public OctreeQuantizer(bool dither)
: this(GetDiffuser(dither), 255)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public OctreeQuantizer(IErrorDiffuser diffuser)
: this(diffuser, 255)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
/// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors)
{
Guard.MustBeBetweenOrEqualTo(maxColors, 1, 255, nameof(maxColors));
this.Diffuser = diffuser;
this.MaxColors = maxColors;
}
/// <inheritdoc />
public IErrorDiffuser Diffuser { get; }
/// <summary>
/// Gets the maximum number of colors to hold in the color palette.
/// </summary>
public int MaxColors { get; }
/// <inheritdoc />
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
=> new OctreeFrameQuantizer<TPixel>(this);
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

65
src/ImageSharp/Processing/Quantization/PaletteQuantizer.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
using SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers;
namespace SixLabors.ImageSharp.Processing.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.
/// <para>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering and the <see cref="NamedColors{TPixel}.WebSafePalette"/>
/// </para>
/// </summary>
public class PaletteQuantizer : IQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
/// </summary>
public PaletteQuantizer()
: this(true)
{
}
/// <summary>
/// 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)
: this(GetDiffuser(dither))
{
}
/// <summary>
/// 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;
}
/// <inheritdoc />
public IErrorDiffuser Diffuser { get; }
/// <summary>
/// Gets the palette to use to quantize the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <returns>The <see cref="T:TPixel[]"/></returns>
public virtual TPixel[] GetPalette<TPixel>()
where TPixel : struct, IPixel<TPixel>
=> NamedColors<TPixel>.WebSafePalette;
/// <inheritdoc />
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
=> new PaletteFrameQuantizer<TPixel>(this);
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

53
src/ImageSharp/Processing/Quantization/Processors/QuantizeProcessor.cs

@ -2,16 +2,16 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Quantization.Processors
{
/// <summary>
/// Enables the quantization of images to remove the number of colors used in the image palette.
/// Enables the quantization of images to reduce the number of colors used in the image palette.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class QuantizeProcessor<TPixel> : ImageProcessor<TPixel>
@ -21,51 +21,38 @@ namespace SixLabors.ImageSharp.Processing.Quantization.Processors
/// Initializes a new instance of the <see cref="QuantizeProcessor{TPixel}"/> class.
/// </summary>
/// <param name="quantizer">The quantizer used to reduce the color palette</param>
/// <param name="maxColors">The maximum number of colors to reduce the palette to</param>
public QuantizeProcessor(IQuantizer<TPixel> quantizer, int maxColors)
public QuantizeProcessor(IQuantizer quantizer)
{
Guard.NotNull(quantizer, nameof(quantizer));
Guard.MustBeGreaterThan(maxColors, 0, nameof(maxColors));
this.Quantizer = quantizer;
this.MaxColors = maxColors;
}
/// <summary>
/// Gets the quantizer
/// </summary>
public IQuantizer<TPixel> Quantizer { get; }
/// <summary>
/// Gets the maximum number of palette colors
/// </summary>
public int MaxColors { get; }
public IQuantizer Quantizer { get; }
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
QuantizedFrame<TPixel> quantized = this.Quantizer.Quantize(source, this.MaxColors);
IFrameQuantizer<TPixel> executor = this.Quantizer.CreateFrameQuantizer<TPixel>();
QuantizedFrame<TPixel> quantized = executor.QuantizeFrame(source);
int paletteCount = quantized.Palette.Length - 1;
using (Buffer2D<TPixel> pixels = source.MemoryManager.Allocate2D<TPixel>(quantized.Width, quantized.Height))
// Not parallel to remove "quantized" closure allocation.
// We can operate directly on the source here as we've already read it to get the
// quantized result
for (int y = 0; y < source.Height; y++)
{
Parallel.For(
0,
pixels.Height,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = pixels.GetRowSpan(y);
int yy = y * pixels.Width;
for (int x = 0; x < pixels.Width; x++)
{
int i = x + yy;
TPixel color = quantized.Palette[Math.Min(paletteCount, quantized.Pixels[i])];
row[x] = color;
}
});
Buffer2D<TPixel>.SwapContents(source.PixelBuffer, pixels);
Span<TPixel> row = source.GetPixelRowSpan(y);
int yy = y * source.Width;
for (int x = 0; x < source.Width; x++)
{
int i = x + yy;
TPixel color = quantized.Palette[Math.Min(paletteCount, quantized.Pixels[i])];
row[x] = color;
}
}
}
}

29
src/ImageSharp/Processing/Quantization/QuantizationMode.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Provides enumeration over how an image should be quantized.
/// </summary>
public enum QuantizationMode
{
/// <summary>
/// An adaptive Octree quantizer. Fast with good quality.
/// The quantizer only supports a single alpha value.
/// </summary>
Octree,
/// <summary>
/// Xiaolin Wu's Color Quantizer which generates high quality output.
/// The quantizer supports multiple alpha values.
/// </summary>
Wu,
/// <summary>
/// Palette based, Uses the collection of web-safe colors by default.
/// The quantizer supports multiple alpha values.
/// </summary>
Palette
}
}

31
src/ImageSharp/Processing/Quantization/QuantizeExtensions.cs

@ -12,34 +12,14 @@ namespace SixLabors.ImageSharp.Processing.Quantization
public static class QuantizeExtensions
{
/// <summary>
/// Applies quantization to the image.
/// Applies quantization to the image using the <see cref="OctreeQuantizer"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="mode">The quantization mode to apply to perform the operation.</param>
/// <param name="maxColors">The maximum number of colors to return. Defaults to 256.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Quantize<TPixel>(this IImageProcessingContext<TPixel> source, QuantizationMode mode = QuantizationMode.Octree, int maxColors = 256)
public static IImageProcessingContext<TPixel> Quantize<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
IQuantizer<TPixel> quantizer;
switch (mode)
{
case QuantizationMode.Wu:
quantizer = new WuQuantizer<TPixel>();
break;
case QuantizationMode.Palette:
quantizer = new PaletteQuantizer<TPixel>();
break;
default:
quantizer = new OctreeQuantizer<TPixel>();
break;
}
return Quantize(source, quantizer, maxColors);
}
=> Quantize(source, KnownQuantizers.Octree);
/// <summary>
/// Applies quantization to the image.
@ -47,10 +27,9 @@ namespace SixLabors.ImageSharp.Processing.Quantization
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="quantizer">The quantizer to apply to perform the operation.</param>
/// <param name="maxColors">The maximum number of colors to return.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Quantize<TPixel>(this IImageProcessingContext<TPixel> source, IQuantizer<TPixel> quantizer, int maxColors)
public static IImageProcessingContext<TPixel> Quantize<TPixel>(this IImageProcessingContext<TPixel> source, IQuantizer quantizer)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new QuantizeProcessor<TPixel>(quantizer, maxColors));
=> source.ApplyProcessor(new QuantizeProcessor<TPixel>(quantizer));
}
}

82
src/ImageSharp/Processing/Quantization/WuQuantizer.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Dithering;
using SixLabors.ImageSharp.Processing.Dithering.ErrorDiffusion;
using SixLabors.ImageSharp.Processing.Quantization.FrameQuantizers;
namespace SixLabors.ImageSharp.Processing.Quantization
{
/// <summary>
/// Allows the quantization of images pixels using Xiaolin Wu's Color Quantizer <see href="http://www.ece.mcmaster.ca/~xwu/cq.c"/>
/// <para>
/// By default the quantizer uses <see cref="KnownDiffusers.FloydSteinberg"/> dithering and a color palette of a maximum length of <value>255</value>
/// </para>
/// </summary>
public class WuQuantizer : IQuantizer
{
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
public WuQuantizer()
: this(true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
/// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
public WuQuantizer(int maxColors)
: this(GetDiffuser(true), maxColors)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
/// <param name="dither">Whether to apply dithering to the output image</param>
public WuQuantizer(bool dither)
: this(GetDiffuser(dither), 255)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
public WuQuantizer(IErrorDiffuser diffuser)
: this(diffuser, 255)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer"/> class.
/// </summary>
/// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
/// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
public WuQuantizer(IErrorDiffuser diffuser, int maxColors)
{
Guard.MustBeBetweenOrEqualTo(maxColors, 1, 255, nameof(maxColors));
this.Diffuser = diffuser;
this.MaxColors = maxColors;
}
/// <inheritdoc />
public IErrorDiffuser Diffuser { get; }
/// <summary>
/// Gets the maximum number of colors to hold in the color palette.
/// </summary>
public int MaxColors { get; }
/// <inheritdoc />
public IFrameQuantizer<TPixel> CreateFrameQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
=> new WuFrameQuantizer<TPixel>(this);
private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null;
}
}

2
src/ImageSharp/Processing/Transforms/AnchorPosition.cs → src/ImageSharp/Processing/Transforms/AnchorPositionMode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Enumerated anchor positions to apply to resized images.
/// </summary>
public enum AnchorPosition
public enum AnchorPositionMode
{
/// <summary>
/// Anchors the position of the image to the center of it's bounding container.

6
src/ImageSharp/Processing/Transforms/FlipExtensions.cs

@ -16,10 +16,10 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to rotate, flip, or both.</param>
/// <param name="flipType">The <see cref="FlipType"/> to perform the flip.</param>
/// <param name="flipMode">The <see cref="FlipMode"/> to perform the flip.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Flip<TPixel>(this IImageProcessingContext<TPixel> source, FlipType flipType)
public static IImageProcessingContext<TPixel> Flip<TPixel>(this IImageProcessingContext<TPixel> source, FlipMode flipMode)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new FlipProcessor<TPixel>(flipType));
=> source.ApplyProcessor(new FlipProcessor<TPixel>(flipMode));
}
}

2
src/ImageSharp/Processing/Transforms/FlipType.cs → src/ImageSharp/Processing/Transforms/FlipMode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Provides enumeration over how a image should be flipped.
/// </summary>
public enum FlipType
public enum FlipMode
{
/// <summary>
/// Don't flip the image.

2
src/ImageSharp/Processing/Transforms/ResampleMode.cs → src/ImageSharp/Processing/Transforms/KnownResamplers.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Contains reusable static instances of known resampling algorithms
/// </summary>
public static class ResampleMode
public static class KnownResamplers
{
/// <summary>
/// Gets the Bicubic sampler that implements the bicubic kernel algorithm W(x)

2
src/ImageSharp/Processing/Transforms/OrientationType.cs → src/ImageSharp/Processing/Transforms/OrientationMode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Enumerates the available orientation values supplied by EXIF metadata.
/// </summary>
internal enum OrientationType : ushort
internal enum OrientationMode : ushort
{
/// <summary>
/// Unknown rotation.

2
src/ImageSharp/Processing/Transforms/PadExtensions.cs

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
{
Size = new Size(width, height),
Mode = ResizeMode.BoxPad,
Sampler = ResampleMode.NearestNeighbor
Sampler = KnownResamplers.NearestNeighbor
};
return source.Resize(options);

54
src/ImageSharp/Processing/Transforms/Processors/AutoOrientProcessor.cs

@ -19,42 +19,42 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
/// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
OrientationType orientation = GetExifOrientation(source);
OrientationMode orientation = GetExifOrientation(source);
Size size = sourceRectangle.Size;
switch (orientation)
{
case OrientationType.TopRight:
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
case OrientationMode.TopRight:
new FlipProcessor<TPixel>(FlipMode.Horizontal).Apply(source, sourceRectangle);
break;
case OrientationType.BottomRight:
new RotateProcessor<TPixel>((int)RotateType.Rotate180, size).Apply(source, sourceRectangle);
case OrientationMode.BottomRight:
new RotateProcessor<TPixel>((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle);
break;
case OrientationType.BottomLeft:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
case OrientationMode.BottomLeft:
new FlipProcessor<TPixel>(FlipMode.Vertical).Apply(source, sourceRectangle);
break;
case OrientationType.LeftTop:
new RotateProcessor<TPixel>((int)RotateType.Rotate90, size).Apply(source, sourceRectangle);
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
case OrientationMode.LeftTop:
new RotateProcessor<TPixel>((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle);
new FlipProcessor<TPixel>(FlipMode.Horizontal).Apply(source, sourceRectangle);
break;
case OrientationType.RightTop:
new RotateProcessor<TPixel>((int)RotateType.Rotate90, size).Apply(source, sourceRectangle);
case OrientationMode.RightTop:
new RotateProcessor<TPixel>((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle);
break;
case OrientationType.RightBottom:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateType.Rotate270, size).Apply(source, sourceRectangle);
case OrientationMode.RightBottom:
new FlipProcessor<TPixel>(FlipMode.Vertical).Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle);
break;
case OrientationType.LeftBottom:
new RotateProcessor<TPixel>((int)RotateType.Rotate270, size).Apply(source, sourceRectangle);
case OrientationMode.LeftBottom:
new RotateProcessor<TPixel>((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle);
break;
case OrientationType.Unknown:
case OrientationType.TopLeft:
case OrientationMode.Unknown:
case OrientationMode.TopLeft:
default:
break;
}
@ -70,32 +70,32 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
/// Returns the current EXIF orientation
/// </summary>
/// <param name="source">The image to auto rotate.</param>
/// <returns>The <see cref="OrientationType"/></returns>
private static OrientationType GetExifOrientation(Image<TPixel> source)
/// <returns>The <see cref="OrientationMode"/></returns>
private static OrientationMode GetExifOrientation(Image<TPixel> source)
{
if (source.MetaData.ExifProfile == null)
{
return OrientationType.Unknown;
return OrientationMode.Unknown;
}
ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation);
if (value == null)
{
return OrientationType.Unknown;
return OrientationMode.Unknown;
}
OrientationType orientation;
OrientationMode orientation;
if (value.DataType == ExifDataType.Short)
{
orientation = (OrientationType)value.Value;
orientation = (OrientationMode)value.Value;
}
else
{
orientation = (OrientationType)Convert.ToUInt16(value.Value);
orientation = (OrientationMode)Convert.ToUInt16(value.Value);
source.MetaData.ExifProfile.RemoveValue(ExifTag.Orientation);
}
source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationType.TopLeft);
source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft);
return orientation;
}

16
src/ImageSharp/Processing/Transforms/Processors/FlipProcessor.cs

@ -21,27 +21,27 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
/// <summary>
/// Initializes a new instance of the <see cref="FlipProcessor{TPixel}"/> class.
/// </summary>
/// <param name="flipType">The <see cref="FlipType"/> used to perform flipping.</param>
public FlipProcessor(FlipType flipType)
/// <param name="flipMode">The <see cref="Transforms.FlipMode"/> used to perform flipping.</param>
public FlipProcessor(FlipMode flipMode)
{
this.FlipType = flipType;
this.FlipMode = flipMode;
}
/// <summary>
/// Gets the <see cref="FlipType"/> used to perform flipping.
/// Gets the <see cref="Transforms.FlipMode"/> used to perform flipping.
/// </summary>
public FlipType FlipType { get; }
public FlipMode FlipMode { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
switch (this.FlipType)
switch (this.FlipMode)
{
// No default needed as we have already set the pixels.
case FlipType.Vertical:
case FlipMode.Vertical:
this.FlipX(source, configuration);
break;
case FlipType.Horizontal:
case FlipMode.Horizontal:
this.FlipY(source, configuration);
break;
}

2
src/ImageSharp/Processing/Transforms/Processors/RotateProcessor.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
/// <param name="degrees">The angle of rotation in degrees.</param>
/// <param name="sourceSize">The source image size</param>
public RotateProcessor(float degrees, Size sourceSize)
: this(degrees, ResampleMode.Bicubic, sourceSize)
: this(degrees, KnownResamplers.Bicubic, sourceSize)
{
}

2
src/ImageSharp/Processing/Transforms/Processors/SkewProcessor.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms.Processors
/// <param name="degreesY">The angle in degrees to perform the skew along the y-axis.</param>
/// <param name="sourceSize">The source image size</param>
public SkewProcessor(float degreesX, float degreesY, Size sourceSize)
: this(degreesX, degreesY, ResampleMode.Bicubic, sourceSize)
: this(degreesX, degreesY, KnownResamplers.Bicubic, sourceSize)
{
}

8
src/ImageSharp/Processing/Transforms/ResizeExtensions.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, size.Width, size.Height, ResampleMode.Bicubic, false);
=> Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false);
/// <summary>
/// Resizes an image to the given <see cref="Size"/>.
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size, bool compand)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, size.Width, size.Height, ResampleMode.Bicubic, compand);
=> Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand);
/// <summary>
/// Resizes an image to the given width and height.
@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, width, height, ResampleMode.Bicubic, false);
=> Resize(source, width, height, KnownResamplers.Bicubic, false);
/// <summary>
/// Resizes an image to the given width and height.
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <remarks>Passing zero for one of height or width will automatically preserve the aspect ratio of the original image</remarks>
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height, bool compand)
where TPixel : struct, IPixel<TPixel>
=> Resize(source, width, height, ResampleMode.Bicubic, compand);
=> Resize(source, width, height, KnownResamplers.Bicubic, compand);
/// <summary>
/// Resizes an image to the given width and height with the given sampler.

64
src/ImageSharp/Processing/Transforms/ResizeHelper.cs

@ -87,14 +87,14 @@ namespace SixLabors.ImageSharp.Processing.Transforms
{
switch (options.Position)
{
case AnchorPosition.Top:
case AnchorPosition.TopLeft:
case AnchorPosition.TopRight:
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
destinationY = 0;
break;
case AnchorPosition.Bottom:
case AnchorPosition.BottomLeft:
case AnchorPosition.BottomRight:
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
break;
default:
@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp.Processing.Transforms
{
switch (options.Position)
{
case AnchorPosition.Left:
case AnchorPosition.TopLeft:
case AnchorPosition.BottomLeft:
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
destinationX = 0;
break;
case AnchorPosition.Right:
case AnchorPosition.TopRight:
case AnchorPosition.BottomRight:
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
break;
default:
@ -177,14 +177,14 @@ namespace SixLabors.ImageSharp.Processing.Transforms
switch (options.Position)
{
case AnchorPosition.Left:
case AnchorPosition.TopLeft:
case AnchorPosition.BottomLeft:
case AnchorPositionMode.Left:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.BottomLeft:
destinationX = 0;
break;
case AnchorPosition.Right:
case AnchorPosition.TopRight:
case AnchorPosition.BottomRight:
case AnchorPositionMode.Right:
case AnchorPositionMode.TopRight:
case AnchorPositionMode.BottomRight:
destinationX = (int)MathF.Round(width - (sourceWidth * ratio));
break;
default:
@ -199,14 +199,14 @@ namespace SixLabors.ImageSharp.Processing.Transforms
switch (options.Position)
{
case AnchorPosition.Top:
case AnchorPosition.TopLeft:
case AnchorPosition.TopRight:
case AnchorPositionMode.Top:
case AnchorPositionMode.TopLeft:
case AnchorPositionMode.TopRight:
destinationY = 0;
break;
case AnchorPosition.Bottom:
case AnchorPosition.BottomLeft:
case AnchorPosition.BottomRight:
case AnchorPositionMode.Bottom:
case AnchorPositionMode.BottomLeft:
case AnchorPositionMode.BottomRight:
destinationY = (int)MathF.Round(height - (sourceHeight * ratio));
break;
default:
@ -247,35 +247,35 @@ namespace SixLabors.ImageSharp.Processing.Transforms
switch (options.Position)
{
case AnchorPosition.Left:
case AnchorPositionMode.Left:
destinationY = (height - sourceHeight) / 2;
destinationX = 0;
break;
case AnchorPosition.Right:
case AnchorPositionMode.Right:
destinationY = (height - sourceHeight) / 2;
destinationX = width - sourceWidth;
break;
case AnchorPosition.TopRight:
case AnchorPositionMode.TopRight:
destinationY = 0;
destinationX = width - sourceWidth;
break;
case AnchorPosition.Top:
case AnchorPositionMode.Top:
destinationY = 0;
destinationX = (width - sourceWidth) / 2;
break;
case AnchorPosition.TopLeft:
case AnchorPositionMode.TopLeft:
destinationY = 0;
destinationX = 0;
break;
case AnchorPosition.BottomRight:
case AnchorPositionMode.BottomRight:
destinationY = height - sourceHeight;
destinationX = width - sourceWidth;
break;
case AnchorPosition.Bottom:
case AnchorPositionMode.Bottom:
destinationY = height - sourceHeight;
destinationX = (width - sourceWidth) / 2;
break;
case AnchorPosition.BottomLeft:
case AnchorPositionMode.BottomLeft:
destinationY = height - sourceHeight;
destinationX = 0;
break;

2
src/ImageSharp/Processing/Transforms/ResizeMode.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Processing.Transforms
{
/// <summary>
/// Enumerated resize modes to apply to images.
/// Provides enumeration over how the image should be resized.
/// </summary>
public enum ResizeMode
{

4
src/ImageSharp/Processing/Transforms/ResizeOptions.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Gets or sets the anchor position.
/// </summary>
public AnchorPosition Position { get; set; } = AnchorPosition.Center;
public AnchorPositionMode Position { get; set; } = AnchorPositionMode.Center;
/// <summary>
/// Gets or sets the center coordinates.
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Gets or sets the sampler to perform the resize operation.
/// </summary>
public IResampler Sampler { get; set; } = ResampleMode.Bicubic;
public IResampler Sampler { get; set; } = KnownResamplers.Bicubic;
/// <summary>
/// Gets or sets a value indicating whether to compress

8
src/ImageSharp/Processing/Transforms/RotateExtensions.cs

@ -17,11 +17,11 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to rotate.</param>
/// <param name="rotateType">The <see cref="RotateType"/> to perform the rotation.</param>
/// <param name="rotateMode">The <see cref="RotateMode"/> to perform the rotation.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, RotateType rotateType)
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, RotateMode rotateMode)
where TPixel : struct, IPixel<TPixel>
=> Rotate(source, (float)rotateType);
=> Rotate(source, (float)rotateMode);
/// <summary>
/// Rotates an image by the given angle in degrees.
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees)
where TPixel : struct, IPixel<TPixel>
=> Rotate(source, degrees, ResampleMode.Bicubic);
=> Rotate(source, degrees, KnownResamplers.Bicubic);
/// <summary>
/// Rotates an image by the given angle in degrees using the specified sampling algorithm.

8
src/ImageSharp/Processing/Transforms/RotateFlipExtensions.cs

@ -15,11 +15,11 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to rotate, flip, or both.</param>
/// <param name="rotateType">The <see cref="RotateType"/> to perform the rotation.</param>
/// <param name="flipType">The <see cref="FlipType"/> to perform the flip.</param>
/// <param name="rotateMode">The <see cref="RotateMode"/> to perform the rotation.</param>
/// <param name="flipMode">The <see cref="FlipMode"/> to perform the flip.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> RotateFlip<TPixel>(this IImageProcessingContext<TPixel> source, RotateType rotateType, FlipType flipType)
public static IImageProcessingContext<TPixel> RotateFlip<TPixel>(this IImageProcessingContext<TPixel> source, RotateMode rotateMode, FlipMode flipMode)
where TPixel : struct, IPixel<TPixel>
=> source.Rotate(rotateType).Flip(flipType);
=> source.Rotate(rotateMode).Flip(flipMode);
}
}

2
src/ImageSharp/Processing/Transforms/RotateType.cs → src/ImageSharp/Processing/Transforms/RotateMode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <summary>
/// Provides enumeration over how the image should be rotated.
/// </summary>
public enum RotateType
public enum RotateMode
{
/// <summary>
/// Do not rotate the image.

2
src/ImageSharp/Processing/Transforms/SkewExtensions.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY)
where TPixel : struct, IPixel<TPixel>
=> Skew(source, degreesX, degreesY, ResampleMode.Bicubic);
=> Skew(source, degreesX, degreesY, KnownResamplers.Bicubic);
/// <summary>
/// Skews an image by the given angles in degrees using the specified sampling algorithm.

4
src/ImageSharp/Processing/Transforms/TransformExtensions.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Transform<TPixel>(this IImageProcessingContext<TPixel> source, Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel>
=> Transform(source, matrix, ResampleMode.Bicubic);
=> Transform(source, matrix, KnownResamplers.Bicubic);
/// <summary>
/// Transforms an image by the given matrix using the specified sampling algorithm.
@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Transforms
/// <returns>The <see cref="Image{TPixel}"/></returns>
internal static IImageProcessingContext<TPixel> Transform<TPixel>(this IImageProcessingContext<TPixel> source, Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel>
=> Transform(source, matrix, ResampleMode.Bicubic);
=> Transform(source, matrix, KnownResamplers.Bicubic);
/// <summary>
/// Applies a projective transform to the image by the given matrix using the specified sampling algorithm.

53
tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs

@ -3,20 +3,15 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using CoreImage = SixLabors.ImageSharp.Image;
namespace SixLabors.ImageSharp.Benchmarks.Image
{
using System.IO;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Processing.Quantization;
using CoreImage = ImageSharp.Image;
/// <summary>
/// Benchmarks saving png files using different quantizers. System.Drawing cannot save indexed png files so we cannot compare.
/// </summary>
@ -35,8 +30,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
if (this.bmpStream == null)
{
string path = this.LargeImage
? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg"
: "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg"
: "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
this.bmpStream = File.OpenRead(path);
this.bmpCore = CoreImage.Load<Rgba32>(this.bmpStream);
this.bmpStream.Position = 0;
@ -53,20 +49,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
[Benchmark(Baseline = true, Description = "ImageSharp Octree Png")]
public void PngCoreOctree()
{
using (MemoryStream memoryStream = new MemoryStream())
using (var memoryStream = new MemoryStream())
{
PngEncoder encoder = new PngEncoder() { Quantizer = new OctreeQuantizer<Rgba32>(), PaletteSize = 256 };
var encoder = new PngEncoder { Quantizer = new OctreeQuantizer() };
this.bmpCore.SaveAsPng(memoryStream, encoder);
}
}
[Benchmark(Description = "ImageSharp Octree NoDither Png")]
public void PngCoreOctreeNoDIther()
public void PngCoreOctreeNoDither()
{
using (MemoryStream memoryStream = new MemoryStream())
using (var memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder() { Quantizer = new OctreeQuantizer<Rgba32> { Dither = false }, PaletteSize = 256 };
var options = new PngEncoder { Quantizer = new OctreeQuantizer(false) };
this.bmpCore.SaveAsPng(memoryStream, options);
}
@ -75,9 +71,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
[Benchmark(Description = "ImageSharp Palette Png")]
public void PngCorePalette()
{
using (MemoryStream memoryStream = new MemoryStream())
using (var memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder() { Quantizer = new PaletteQuantizer<Rgba32>(), PaletteSize = 256 };
var options = new PngEncoder { Quantizer = new PaletteQuantizer() };
this.bmpCore.SaveAsPng(memoryStream, options);
}
@ -86,9 +82,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
[Benchmark(Description = "ImageSharp Palette NoDither Png")]
public void PngCorePaletteNoDither()
{
using (MemoryStream memoryStream = new MemoryStream())
using (var memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder() { Quantizer = new PaletteQuantizer<Rgba32> { Dither = false }, PaletteSize = 256 };
var options = new PngEncoder { Quantizer = new PaletteQuantizer(false) };
this.bmpCore.SaveAsPng(memoryStream, options);
}
@ -97,9 +93,20 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
[Benchmark(Description = "ImageSharp Wu Png")]
public void PngCoreWu()
{
using (MemoryStream memoryStream = new MemoryStream())
using (var memoryStream = new MemoryStream())
{
var options = new PngEncoder { Quantizer = new WuQuantizer() };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}
[Benchmark(Description = "ImageSharp Wu NoDither Png")]
public void PngCoreWuNoDither()
{
using (var memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder() { Quantizer = new WuQuantizer<Rgba32>(), PaletteSize = 256 };
var options = new PngEncoder { Quantizer = new WuQuantizer(false) };
this.bmpCore.SaveAsPng(memoryStream, options);
}

10
tests/ImageSharp.Benchmarks/Image/EncodePng.cs

@ -70,12 +70,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Image
{
using (var memoryStream = new MemoryStream())
{
QuantizerBase<Rgba32> quantizer = this.UseOctreeQuantizer
? (QuantizerBase<Rgba32>)
new OctreeQuantizer<Rgba32>()
: new PaletteQuantizer<Rgba32>();
IQuantizer quantizer = this.UseOctreeQuantizer
?
(IQuantizer)new OctreeQuantizer()
: new PaletteQuantizer();
var options = new PngEncoder() { Quantizer = quantizer };
var options = new PngEncoder { Quantizer = quantizer };
this.bmpCore.SaveAsPng(memoryStream, options);
}
}

2
tests/ImageSharp.Tests/Drawing/DrawImageTest.cs

@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests
// We pass a new rectangle here based on the dest bounds since we've offset the matrix
blend.Mutate(x => x.Transform(
centeredMatrix,
ResampleMode.Bicubic,
KnownResamplers.Bicubic,
new Rectangle(0, 0, destBounds.Width, destBounds.Height)));
var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2);

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

@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests
{
using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}"))
{
image.Mutate(x => x.Quantize(QuantizationMode.Octree));
image.Mutate(x => x.Quantize(KnownQuantizers.Octree));
image.Save(output, mimeType);
}
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests
{
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
{
image.Mutate(x => x.Quantize(QuantizationMode.Wu));
image.Mutate(x => x.Quantize(KnownQuantizers.Wu));
image.Save(output, mimeType);
}
}
@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests
{
using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}"))
{
image.Mutate(x => x.Quantize(QuantizationMode.Palette));
image.Mutate(x => x.Quantize(KnownQuantizers.Palette));
image.Save(output, mimeType);
}
}

64
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -7,6 +7,8 @@ using System.Linq;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
using Xunit;
// ReSharper disable InconsistentNaming
@ -22,31 +24,31 @@ namespace SixLabors.ImageSharp.Tests
/// <summary>
/// All types except Palette
/// </summary>
public static readonly TheoryData<PngColorType> PngColorTypes = new TheoryData<PngColorType>()
{
PngColorType.RgbWithAlpha,
PngColorType.Rgb,
PngColorType.Grayscale,
PngColorType.GrayscaleWithAlpha,
};
public static readonly TheoryData<PngColorType> PngColorTypes = new TheoryData<PngColorType>
{
PngColorType.RgbWithAlpha,
PngColorType.Rgb,
PngColorType.Grayscale,
PngColorType.GrayscaleWithAlpha,
};
/// <summary>
/// All types except Palette
/// </summary>
public static readonly TheoryData<int> CompressionLevels = new TheoryData<int>()
{
1, 2, 3, 4, 5, 6, 7, 8, 9
};
public static readonly TheoryData<int> CompressionLevels = new TheoryData<int>
{
1, 2, 3, 4, 5, 6, 7, 8, 9
};
public static readonly TheoryData<int> PaletteSizes = new TheoryData<int>()
public static readonly TheoryData<int> PaletteSizes = new TheoryData<int>
{
30, 55, 100, 201, 255
};
30, 55, 100, 201, 255
};
public static readonly TheoryData<int> PaletteLargeOnly = new TheoryData<int>()
{
80, 100, 120, 230
};
public static readonly TheoryData<int> PaletteLargeOnly = new TheoryData<int>
{
80, 100, 120, 230
};
[Theory]
[WithFile(TestImages.Png.Palette8Bpp, nameof(PngColorTypes), PixelTypes.Rgba32)]
@ -60,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests
{
TestPngEncoderCore(provider, pngColorType, appendPngColorType: true);
}
[Theory]
[WithTestPatternImages(nameof(PngColorTypes), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)]
public void IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider, PngColorType pngColorType)
@ -76,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests
{
TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true);
}
[Theory]
[WithFile(TestImages.Png.Palette8Bpp, nameof(PaletteLargeOnly), PixelTypes.Rgba32)]
public void PaletteColorType_WuQuantizer<TPixel>(TestImageProvider<TPixel> provider, int paletteSize)
@ -92,7 +94,7 @@ namespace SixLabors.ImageSharp.Tests
TestImageProvider<TPixel> provider,
PngColorType pngColorType,
int compressionLevel = 6,
int paletteSize = 0,
int paletteSize = 255,
bool appendPngColorType = false,
bool appendPixelType = false,
bool appendCompressionLevel = false,
@ -107,11 +109,11 @@ namespace SixLabors.ImageSharp.Tests
}
var encoder = new PngEncoder
{
PngColorType = pngColorType,
CompressionLevel = compressionLevel,
PaletteSize = paletteSize
};
{
PngColorType = pngColorType,
CompressionLevel = compressionLevel,
Quantizer = new WuQuantizer(paletteSize)
};
string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : "";
string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : "";
@ -121,10 +123,10 @@ namespace SixLabors.ImageSharp.Tests
// Does DebugSave & load reference CompareToReferenceInput():
string actualOutputFile = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType);
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", debugInfo, appendPixelType);
using (var actualImage = Image.Load<TPixel>(actualOutputFile, referenceDecoder))
using (var referenceImage = Image.Load<TPixel>(referenceOutputFile, referenceDecoder))
{
@ -136,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
}
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32)]
public void WritesFileMarker<TPixel>(TestImageProvider<TPixel> provider)
@ -146,8 +148,8 @@ namespace SixLabors.ImageSharp.Tests
using (var ms = new MemoryStream())
{
image.Save(ms, new PngEncoder());
byte[] data = ms.ToArray().Take(8).ToArray();
byte[] data = ms.ToArray().Take(8).ToArray();
byte[] expected = {
0x89, // Set the high bit.
0x50, // P

2
tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests
this.collection.AddFrame(new Rgba32[0]);
});
Assert.StartsWith("Value must be greater than or equal to 100.", ex.Message);
Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message);
}
[Fact]

2
tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs

@ -6,6 +6,8 @@ using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests

2
tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs

@ -9,6 +9,8 @@ using System.Text;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests

2
tests/ImageSharp.Tests/Numerics/RationalTests.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests

2
tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests

4
tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs

@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
public BinaryDitherTest()
{
this.orderedDither = DitherMode.BayerDither4x4;
this.errorDiffuser = DiffuseMode.FloydSteinberg;
this.orderedDither = KnownDitherers.BayerDither4x4;
this.errorDiffuser = KnownDiffusers.FloydSteinberg;
}
[Fact]

4
tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs

@ -25,8 +25,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
public DitherTest()
{
this.orderedDither = DitherMode.BayerDither4x4;
this.errorDiffuser = DiffuseMode.FloydSteinberg;
this.orderedDither = KnownDitherers.BayerDither4x4;
this.errorDiffuser = KnownDiffusers.FloydSteinberg;
}
[Fact]

20
tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs

@ -17,19 +17,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters
public class ColorBlindnessTest : BaseImageOperationsExtensionTest
{
public static IEnumerable<object[]> TheoryData = new[] {
new object[]{ new TestType<AchromatomalyProcessor<Rgba32>>(), ColorBlindness.Achromatomaly },
new object[]{ new TestType<AchromatopsiaProcessor<Rgba32>>(), ColorBlindness.Achromatopsia },
new object[]{ new TestType<DeuteranomalyProcessor<Rgba32>>(), ColorBlindness.Deuteranomaly },
new object[]{ new TestType<DeuteranopiaProcessor<Rgba32>>(), ColorBlindness.Deuteranopia },
new object[]{ new TestType<ProtanomalyProcessor<Rgba32>>(), ColorBlindness.Protanomaly },
new object[]{ new TestType<ProtanopiaProcessor<Rgba32>>(), ColorBlindness.Protanopia },
new object[]{ new TestType<TritanomalyProcessor<Rgba32>>(), ColorBlindness.Tritanomaly },
new object[]{ new TestType<TritanopiaProcessor<Rgba32>>(), ColorBlindness.Tritanopia }
new object[]{ new TestType<AchromatomalyProcessor<Rgba32>>(), ColorBlindnessMode.Achromatomaly },
new object[]{ new TestType<AchromatopsiaProcessor<Rgba32>>(), ColorBlindnessMode.Achromatopsia },
new object[]{ new TestType<DeuteranomalyProcessor<Rgba32>>(), ColorBlindnessMode.Deuteranomaly },
new object[]{ new TestType<DeuteranopiaProcessor<Rgba32>>(), ColorBlindnessMode.Deuteranopia },
new object[]{ new TestType<ProtanomalyProcessor<Rgba32>>(), ColorBlindnessMode.Protanomaly },
new object[]{ new TestType<ProtanopiaProcessor<Rgba32>>(), ColorBlindnessMode.Protanopia },
new object[]{ new TestType<TritanomalyProcessor<Rgba32>>(), ColorBlindnessMode.Tritanomaly },
new object[]{ new TestType<TritanopiaProcessor<Rgba32>>(), ColorBlindnessMode.Tritanopia }
};
[Theory]
[MemberData(nameof(TheoryData))]
public void ColorBlindness_CorrectProcessor<T>(TestType<T> testType, ColorBlindness colorBlindness)
public void ColorBlindness_CorrectProcessor<T>(TestType<T> testType, ColorBlindnessMode colorBlindness)
where T : IImageProcessor<Rgba32>
{
this.operations.ColorBlindness(colorBlindness);
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters
}
[Theory]
[MemberData(nameof(TheoryData))]
public void ColorBlindness_rect_CorrectProcessor<T>(TestType<T> testType, ColorBlindness colorBlindness)
public void ColorBlindness_rect_CorrectProcessor<T>(TestType<T> testType, ColorBlindnessMode colorBlindness)
where T : IImageProcessor<Rgba32>
{
this.operations.ColorBlindness(colorBlindness, this.rect);

4
tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs

@ -16,14 +16,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters
[Fact]
public void Filter_CorrectProcessor()
{
this.operations.Filter(MatrixFilters.AchromatomalyFilter * MatrixFilters.CreateHueFilter(90F));
this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F));
FilterProcessor<Rgba32> p = this.Verify<FilterProcessor<Rgba32>>();
}
[Fact]
public void Filter_rect_CorrectProcessor()
{
this.operations.Filter(MatrixFilters.AchromatomalyFilter * MatrixFilters.CreateHueFilter(90F), this.rect);
this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F), this.rect);
FilterProcessor<Rgba32> p = this.Verify<FilterProcessor<Rgba32>>(this.rect);
}
}

30
tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs

@ -25,29 +25,29 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
public static readonly TheoryData<string, IOrderedDither> OrderedDitherers = new TheoryData<string, IOrderedDither>
{
{ "Bayer8x8", DitherMode.BayerDither8x8 },
{ "Bayer4x4", DitherMode.BayerDither4x4 },
{ "Ordered3x3", DitherMode.OrderedDither3x3 },
{ "Bayer2x2", DitherMode.BayerDither2x2 }
{ "Bayer8x8", KnownDitherers.BayerDither8x8 },
{ "Bayer4x4", KnownDitherers.BayerDither4x4 },
{ "Ordered3x3", KnownDitherers.OrderedDither3x3 },
{ "Bayer2x2", KnownDitherers.BayerDither2x2 }
};
public static readonly TheoryData<string, IErrorDiffuser> ErrorDiffusers = new TheoryData<string, IErrorDiffuser>
{
{ "Atkinson", DiffuseMode.Atkinson },
{ "Burks", DiffuseMode.Burks },
{ "FloydSteinberg", DiffuseMode.FloydSteinberg },
{ "JarvisJudiceNinke", DiffuseMode.JarvisJudiceNinke },
{ "Sierra2", DiffuseMode.Sierra2 },
{ "Sierra3", DiffuseMode.Sierra3 },
{ "SierraLite", DiffuseMode.SierraLite },
{ "StevensonArce", DiffuseMode.StevensonArce },
{ "Stucki", DiffuseMode.Stucki },
{ "Atkinson", KnownDiffusers.Atkinson },
{ "Burks", KnownDiffusers.Burks },
{ "FloydSteinberg", KnownDiffusers.FloydSteinberg },
{ "JarvisJudiceNinke", KnownDiffusers.JarvisJudiceNinke },
{ "Sierra2", KnownDiffusers.Sierra2 },
{ "Sierra3", KnownDiffusers.Sierra3 },
{ "SierraLite", KnownDiffusers.SierraLite },
{ "StevensonArce", KnownDiffusers.StevensonArce },
{ "Stucki", KnownDiffusers.Stucki },
};
private static IOrderedDither DefaultDitherer => DitherMode.BayerDither4x4;
private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4;
private static IErrorDiffuser DefaultErrorDiffuser => DiffuseMode.Atkinson;
private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson;
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), DefaultPixelType)]

30
tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs

@ -24,29 +24,29 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
public static readonly TheoryData<string, IOrderedDither> OrderedDitherers = new TheoryData<string, IOrderedDither>
{
{ "Bayer8x8", DitherMode.BayerDither8x8 },
{ "Bayer4x4", DitherMode.BayerDither4x4 },
{ "Ordered3x3", DitherMode.OrderedDither3x3 },
{ "Bayer2x2", DitherMode.BayerDither2x2 }
{ "Bayer8x8", KnownDitherers.BayerDither8x8 },
{ "Bayer4x4", KnownDitherers.BayerDither4x4 },
{ "Ordered3x3", KnownDitherers.OrderedDither3x3 },
{ "Bayer2x2", KnownDitherers.BayerDither2x2 }
};
public static readonly TheoryData<string, IErrorDiffuser> ErrorDiffusers = new TheoryData<string, IErrorDiffuser>
{
{ "Atkinson", DiffuseMode.Atkinson },
{ "Burks", DiffuseMode.Burks },
{ "FloydSteinberg", DiffuseMode.FloydSteinberg },
{ "JarvisJudiceNinke", DiffuseMode.JarvisJudiceNinke },
{ "Sierra2", DiffuseMode.Sierra2 },
{ "Sierra3", DiffuseMode.Sierra3 },
{ "SierraLite", DiffuseMode.SierraLite },
{ "StevensonArce", DiffuseMode.StevensonArce },
{ "Stucki", DiffuseMode.Stucki },
{ "Atkinson", KnownDiffusers.Atkinson },
{ "Burks", KnownDiffusers.Burks },
{ "FloydSteinberg", KnownDiffusers.FloydSteinberg },
{ "JarvisJudiceNinke", KnownDiffusers.JarvisJudiceNinke },
{ "Sierra2", KnownDiffusers.Sierra2 },
{ "Sierra3", KnownDiffusers.Sierra3 },
{ "SierraLite", KnownDiffusers.SierraLite },
{ "StevensonArce", KnownDiffusers.StevensonArce },
{ "Stucki", KnownDiffusers.Stucki },
};
private static IOrderedDither DefaultDitherer => DitherMode.BayerDither4x4;
private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4;
private static IErrorDiffuser DefaultErrorDiffuser => DiffuseMode.Atkinson;
private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson;
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(OrderedDitherers), DefaultPixelType)]

22
tests/ImageSharp.Tests/Processing/Processors/Filters/ColorBlindnessTest.cs

@ -13,22 +13,22 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
[GroupOutput("Filters")]
public class ColorBlindnessTest
{
public static readonly TheoryData<ColorBlindness> ColorBlindnessFilters
= new TheoryData<ColorBlindness>
public static readonly TheoryData<ColorBlindnessMode> ColorBlindnessFilters
= new TheoryData<ColorBlindnessMode>
{
ColorBlindness.Achromatomaly,
ColorBlindness.Achromatopsia,
ColorBlindness.Deuteranomaly,
ColorBlindness.Deuteranopia,
ColorBlindness.Protanomaly,
ColorBlindness.Protanopia,
ColorBlindness.Tritanomaly,
ColorBlindness.Tritanopia
ColorBlindnessMode.Achromatomaly,
ColorBlindnessMode.Achromatopsia,
ColorBlindnessMode.Deuteranomaly,
ColorBlindnessMode.Deuteranopia,
ColorBlindnessMode.Protanomaly,
ColorBlindnessMode.Protanopia,
ColorBlindnessMode.Tritanomaly,
ColorBlindnessMode.Tritanopia
};
[Theory]
[WithTestPatternImages(nameof(ColorBlindnessFilters), 48, 48, PixelTypes.Rgba32)]
public void ApplyColorBlindnessFilter<TPixel>(TestImageProvider<TPixel> provider, ColorBlindness colorBlindness)
public void ApplyColorBlindnessFilter<TPixel>(TestImageProvider<TPixel> provider, ColorBlindnessMode colorBlindness)
where TPixel : struct, IPixel<TPixel>
{
provider.RunValidatingProcessorTest(x => x.ColorBlindness(colorBlindness), colorBlindness.ToString());

6
tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs

@ -40,9 +40,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
private static Matrix4x4 CreateCombinedTestFilterMatrix()
{
Matrix4x4 brightness = MatrixFilters.CreateBrightnessFilter(0.9F);
Matrix4x4 hue = MatrixFilters.CreateHueFilter(180F);
Matrix4x4 saturation = MatrixFilters.CreateSaturateFilter(1.5F);
Matrix4x4 brightness = KnownFilterMatrices.CreateBrightnessFilter(0.9F);
Matrix4x4 hue = KnownFilterMatrices.CreateHueFilter(180F);
Matrix4x4 saturation = KnownFilterMatrices.CreateSaturateFilter(1.5F);
Matrix4x4 m = brightness * hue * saturation;
return m;
}

24
tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs

@ -15,18 +15,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
public static readonly string[] FlipFiles = { TestImages.Bmp.F };
public static readonly TheoryData<RotateType, FlipType, ushort> OrientationValues
= new TheoryData<RotateType, FlipType, ushort>
public static readonly TheoryData<RotateMode, FlipMode, ushort> OrientationValues
= new TheoryData<RotateMode, FlipMode, ushort>
{
{ RotateType.None, FlipType.None, 0 },
{ RotateType.None, FlipType.None, 1 },
{ RotateType.None, FlipType.Horizontal, 2 },
{ RotateType.Rotate180, FlipType.None, 3 },
{ RotateType.Rotate180, FlipType.Horizontal, 4 },
{ RotateType.Rotate90, FlipType.Horizontal, 5 },
{ RotateType.Rotate270, FlipType.None, 6 },
{ RotateType.Rotate90, FlipType.Vertical, 7 },
{ RotateType.Rotate90, FlipType.None, 8 },
{ RotateMode.None, FlipMode.None, 0 },
{ RotateMode.None, FlipMode.None, 1 },
{ RotateMode.None, FlipMode.Horizontal, 2 },
{ RotateMode.Rotate180, FlipMode.None, 3 },
{ RotateMode.Rotate180, FlipMode.Horizontal, 4 },
{ RotateMode.Rotate90, FlipMode.Horizontal, 5 },
{ RotateMode.Rotate270, FlipMode.None, 6 },
{ RotateMode.Rotate90, FlipMode.Vertical, 7 },
{ RotateMode.Rotate90, FlipMode.None, 8 },
};
public static readonly TheoryData<ExifDataType, byte[]> InvalidOrientationValues
@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[Theory]
[WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)]
public void ImageShouldAutoRotate<TPixel>(TestImageProvider<TPixel> provider, RotateType rotateType, FlipType flipType, ushort orientation)
public void ImageShouldAutoRotate<TPixel>(TestImageProvider<TPixel> provider, RotateMode rotateType, FlipMode flipType, ushort orientation)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())

12
tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs

@ -13,17 +13,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
public static readonly string[] FlipFiles = { TestImages.Bmp.F };
public static readonly TheoryData<FlipType> FlipValues
= new TheoryData<FlipType>
public static readonly TheoryData<FlipMode> FlipValues
= new TheoryData<FlipMode>
{
{ FlipType.None },
{ FlipType.Vertical },
{ FlipType.Horizontal },
{ FlipMode.None },
{ FlipMode.Vertical },
{ FlipMode.Horizontal },
};
[Theory]
[WithFileCollection(nameof(FlipFiles), nameof(FlipValues), DefaultPixelType)]
public void ImageShouldFlip<TPixel>(TestImageProvider<TPixel> provider, FlipType flipType)
public void ImageShouldFlip<TPixel>(TestImageProvider<TPixel> provider, FlipMode flipType)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())

2
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs

@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public void PrintWeightsData()
{
var size = new Size(500, 500);
var proc = new ResizeProcessor<Rgba32>(ResampleMode.Bicubic, 200, 200, size);
var proc = new ResizeProcessor<Rgba32>(KnownResamplers.Bicubic, 200, 200, size);
WeightsBuffer weights = proc.PrecomputeWeights(Configuration.Default.MemoryManager, proc.Width, size.Width);

38
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -20,20 +20,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public static readonly TheoryData<string, IResampler> AllReSamplers =
new TheoryData<string, IResampler>
{
{ "Bicubic", ResampleMode.Bicubic },
{ "Triangle", ResampleMode.Triangle},
{ "NearestNeighbor", ResampleMode.NearestNeighbor },
{ "Box", ResampleMode.Box },
{ "Bicubic", KnownResamplers.Bicubic },
{ "Triangle", KnownResamplers.Triangle},
{ "NearestNeighbor", KnownResamplers.NearestNeighbor },
{ "Box", KnownResamplers.Box },
// { "Lanczos2", KnownResamplers.Lanczos2 }, TODO: Add expected file
{ "Lanczos3", ResampleMode.Lanczos3 },
{ "Lanczos5", ResampleMode.Lanczos5 },
{ "MitchellNetravali", ResampleMode.MitchellNetravali },
{ "Lanczos8", ResampleMode.Lanczos8 },
{ "Hermite", ResampleMode.Hermite },
{ "Spline", ResampleMode.Spline },
{ "Robidoux", ResampleMode.Robidoux },
{ "RobidouxSharp", ResampleMode.RobidouxSharp },
{ "Welch", ResampleMode.Welch }
{ "Lanczos3", KnownResamplers.Lanczos3 },
{ "Lanczos5", KnownResamplers.Lanczos5 },
{ "MitchellNetravali", KnownResamplers.MitchellNetravali },
{ "Lanczos8", KnownResamplers.Lanczos8 },
{ "Hermite", KnownResamplers.Hermite },
{ "Spline", KnownResamplers.Spline },
{ "Robidoux", KnownResamplers.Robidoux },
{ "RobidouxSharp", KnownResamplers.RobidouxSharp },
{ "Welch", KnownResamplers.Welch }
};
[Theory]
@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, ResampleMode.NearestNeighbor));
image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, KnownResamplers.NearestNeighbor));
// Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :(
image.DebugSave(provider, extension: Extensions.Gif);
@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4);
var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2);
image.Mutate(x => x.Resize(image.Width, image.Height, ResampleMode.Bicubic, sourceRectangle, destRectangle, false));
image.Mutate(x => x.Resize(image.Width, image.Height, KnownResamplers.Bicubic, sourceRectangle, destRectangle, false));
image.DebugSave(provider);
image.CompareToReferenceOutput(provider);
@ -300,7 +300,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void BicubicWindowOscillatesCorrectly(float x, float expected)
{
var sampler = ResampleMode.Bicubic;
var sampler = KnownResamplers.Bicubic;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void TriangleWindowOscillatesCorrectly(float x, float expected)
{
var sampler = ResampleMode.Triangle;
var sampler = KnownResamplers.Triangle;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -328,7 +328,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected)
{
var sampler = ResampleMode.Lanczos3;
var sampler = KnownResamplers.Lanczos3;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -342,7 +342,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(4, 0)]
public static void Lanczos5WindowOscillatesCorrectly(float x, float expected)
{
var sampler = ResampleMode.Lanczos5;
var sampler = KnownResamplers.Lanczos5;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);

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

Loading…
Cancel
Save