Browse Source

Add Filter extension method + rename

af/merge-core
James Jackson-South 8 years ago
parent
commit
1cfe9bb56c
  1. 2
      src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs
  2. 47
      src/ImageSharp/Processing/ColorMatrix/Filter.cs
  3. 121
      src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs
  4. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs
  5. 34
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs
  6. 31
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs
  7. 78
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
  8. 27
      src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs
  9. 2
      src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs
  10. 2
      src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs
  11. 23
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs
  12. 23
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs
  13. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs
  14. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs
  15. 23
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs
  16. 21
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs
  17. 0
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md
  18. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs
  19. 22
      src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs
  20. 2
      src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs
  21. 0
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
  22. 2
      src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs
  23. 2
      src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs
  24. 2
      src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs
  25. 2
      src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs
  26. 2
      src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs
  27. 2
      src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
  28. 2
      src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs
  29. 2
      src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
  30. 2
      src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs
  31. 2
      src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs
  32. 8
      tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs
  33. 0
      tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs
  34. 2
      tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs
  35. 0
      tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs
  36. 26
      tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs
  37. 2
      tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs
  38. 2
      tests/ImageSharp.Tests/Processing/Filters/HueTest.cs
  39. 0
      tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs
  40. 2
      tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs
  41. 0
      tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs
  42. 0
      tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs
  43. 2
      tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs
  44. 2
      tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs
  45. 2
      tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs
  46. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs
  47. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs
  48. 44
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/FilterTest.cs
  49. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs
  50. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs
  51. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs
  52. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs
  53. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs
  54. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturateTest.cs
  55. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs

2
src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs

@ -1,9 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;

47
src/ImageSharp/Processing/ColorMatrix/Filter.cs

@ -0,0 +1,47 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="Image{TPixel}"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Filters an image but the given color matrix
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="matrix">The filter color matrix</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Filter<TPixel>(this IImageProcessingContext<TPixel> source, Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new FilterProcessor<TPixel>(matrix));
return source;
}
/// <summary>
/// Filters an image but the given color matrix
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="matrix">The filter color matrix</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> Filter<TPixel>(this IImageProcessingContext<TPixel> source, Matrix4x4 matrix, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new FilterProcessor<TPixel>(matrix), rectangle);
return source;
}
}
}

121
src/ImageSharp/Processing/ColorMatrix/Matrix4x4Extensions.cs → src/ImageSharp/Processing/ColorMatrix/MatrixFilters.cs

@ -9,9 +9,126 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Provides extensions methods for the <see cref="Matrix4x4"/> struct
/// </summary>
// ReSharper disable once InconsistentNaming
public static class Matrix4x4Extensions
public static class MatrixFilters
{
/// <summary>
/// Gets a filter recreating Achromatomaly (Color desensitivity) color blindness
/// </summary>
public static Matrix4x4 AchromatomalyFilter { get; } = new Matrix4x4
{
M11 = .618F,
M12 = .163F,
M13 = .163F,
M21 = .320F,
M22 = .775F,
M23 = .320F,
M31 = .062F,
M32 = .062F,
M33 = .516F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
public static Matrix4x4 AchromatopsiaFilter { get; } = new Matrix4x4
{
M11 = .299F,
M12 = .299F,
M13 = .299F,
M21 = .587F,
M22 = .587F,
M23 = .587F,
M31 = .114F,
M32 = .114F,
M33 = .114F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Deuteranomaly (Green-Weak) color blindness.
/// </summary>
public static Matrix4x4 DeuteranomalyFilter { get; } = new Matrix4x4
{
M11 = 0.8F,
M12 = 0.258F,
M21 = 0.2F,
M22 = 0.742F,
M23 = 0.142F,
M33 = 0.858F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Deuteranopia (Green-Blind) color blindness.
/// </summary>
public static Matrix4x4 DeuteranopiaFilter { get; } = new Matrix4x4
{
M11 = 0.625F,
M12 = 0.7F,
M21 = 0.375F,
M22 = 0.3F,
M23 = 0.3F,
M33 = 0.7F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Protanomaly (Red-Weak) color blindness.
/// </summary>
public static Matrix4x4 ProtanomalyFilter { get; } = new Matrix4x4
{
M11 = 0.817F,
M12 = 0.333F,
M21 = 0.183F,
M22 = 0.667F,
M23 = 0.125F,
M33 = 0.875F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Protanopia (Red-Blind) color blindness.
/// </summary>
public static Matrix4x4 ProtanopiaFilter { get; } = new Matrix4x4
{
M11 = 0.567F,
M12 = 0.558F,
M21 = 0.433F,
M22 = 0.442F,
M23 = 0.242F,
M33 = 0.758F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Tritanomaly (Blue-Weak) color blindness.
/// </summary>
public static Matrix4x4 TritanomalyFilter { get; } = new Matrix4x4
{
M11 = 0.967F,
M21 = 0.33F,
M22 = 0.733F,
M23 = 0.183F,
M32 = 0.267F,
M33 = 0.817F,
M44 = 1
};
/// <summary>
/// Gets a filter recreating Tritanopia (Blue-Blind) color blindness.
/// </summary>
public static Matrix4x4 TritanopiaFilter { get; } = new Matrix4x4
{
M11 = 0.95F,
M21 = 0.05F,
M22 = 0.433F,
M23 = 0.475F,
M32 = 0.567F,
M33 = 0.525F,
M44 = 1
};
/// <summary>
/// Gets an approximated black and white filter
/// </summary>

34
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .618F,
M12 = .163F,
M13 = .163F,
M21 = .320F,
M22 = .775F,
M23 = .320F,
M31 = .062F,
M32 = .062F,
M33 = .516F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

34
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatopsiaProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = .299F,
M12 = .299F,
M13 = .299F,
M21 = .587F,
M22 = .587F,
M23 = .587F,
M31 = .114F,
M32 = .114F,
M33 = .114F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

31
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs

@ -1,31 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProtanomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
{
M11 = 0.817F,
M12 = 0.333F,
M21 = 0.183F,
M22 = 0.667F,
M23 = 0.125F,
M33 = 0.875F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}

78
src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs

@ -1,78 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// The color matrix filter. Inherit from this class to perform operation involving color matrices.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal abstract class ColorMatrixProcessor<TPixel> : ImageProcessor<TPixel>, IColorMatrixProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public abstract Matrix4x4 Matrix { get; }
/// <inheritdoc/>
public virtual bool Compand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
Matrix4x4 matrix = this.Matrix;
bool compand = this.Compand;
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
Span<TPixel> row = source.GetPixelRowSpan(y - startY);
for (int x = minX; x < maxX; x++)
{
ref TPixel pixel = ref row[x - startX];
var vector = pixel.ToVector4();
if (compand)
{
vector = vector.Expand();
}
vector = Vector4.Transform(vector, matrix);
pixel.PackFromVector4(compand ? vector.Compress() : vector);
}
});
}
}
}

27
src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs

@ -1,27 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Encapsulates properties and methods for creating processors that utilize a matrix to
/// alter the image pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal interface IColorMatrixProcessor<TPixel> : IImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the <see cref="Matrix4x4"/> used to alter the image.
/// </summary>
Matrix4x4 Matrix { get; }
/// <summary>
/// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing.
/// </summary>
bool Compand { get; set; }
}
}

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

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

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

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

23
src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatomalyProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AchromatomalyProcessor{TPixel}"/> class.
/// </summary>
public AchromatomalyProcessor()
: base(MatrixFilters.AchromatomalyFilter)
{
}
}
}

23
src/ImageSharp/Processing/Processors/Filters/ColorBlindness/AchromatopsiaProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AchromatopsiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AchromatopsiaProcessor{TPixel}"/> class.
/// </summary>
public AchromatopsiaProcessor()
: base(MatrixFilters.AchromatopsiaFilter)
{
}
}
}

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranomalyProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DeuteranomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel>
internal class DeuteranomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
/// <summary>
/// Initializes a new instance of the <see cref="DeuteranomalyProcessor{TPixel}"/> class.
/// </summary>
public DeuteranomalyProcessor()
: base(MatrixFilters.DeuteranomalyFilter)
{
M11 = 0.8F,
M12 = 0.258F,
M21 = 0.2F,
M22 = 0.742F,
M23 = 0.142F,
M33 = 0.858F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}
}

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/DeuteranopiaProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DeuteranopiaProcessor<TPixel> : ColorMatrixProcessor<TPixel>
internal class DeuteranopiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
/// <summary>
/// Initializes a new instance of the <see cref="DeuteranopiaProcessor{TPixel}"/> class.
/// </summary>
public DeuteranopiaProcessor()
: base(MatrixFilters.DeuteranopiaFilter)
{
M11 = 0.625F,
M12 = 0.7F,
M21 = 0.375F,
M22 = 0.3F,
M23 = 0.3F,
M33 = 0.7F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}
}

23
src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanomalyProcessor.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProtanomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ProtanomalyProcessor{TPixel}"/> class.
/// </summary>
public ProtanomalyProcessor()
: base(MatrixFilters.ProtanomalyFilter)
{
}
}
}

21
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/ProtanopiaProcessor.cs

@ -10,22 +10,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProtanopiaProcessor<TPixel> : ColorMatrixProcessor<TPixel>
internal class ProtanopiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
/// <summary>
/// Initializes a new instance of the <see cref="ProtanopiaProcessor{TPixel}"/> class.
/// </summary>
public ProtanopiaProcessor()
: base(MatrixFilters.ProtanopiaFilter)
{
M11 = 0.567F,
M12 = 0.558F,
M21 = 0.433F,
M22 = 0.442F,
M23 = 0.242F,
M33 = 0.758F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}
}

0
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/README.md → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/README.md

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanomalyProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class TritanomalyProcessor<TPixel> : ColorMatrixProcessor<TPixel>
internal class TritanomalyProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
/// <summary>
/// Initializes a new instance of the <see cref="TritanomalyProcessor{TPixel}"/> class.
/// </summary>
public TritanomalyProcessor()
: base(MatrixFilters.TritanomalyFilter)
{
M11 = 0.967F,
M21 = 0.33F,
M22 = 0.733F,
M23 = 0.183F,
M32 = 0.267F,
M33 = 0.817F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}
}

22
src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs → src/ImageSharp/Processing/Processors/Filters/ColorBlindness/TritanopiaProcessor.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
@ -10,22 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class TritanopiaProcessor<TPixel> : ColorMatrixProcessor<TPixel>
internal class TritanopiaProcessor<TPixel> : FilterProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
public override Matrix4x4 Matrix => new Matrix4x4
/// <summary>
/// Initializes a new instance of the <see cref="TritanopiaProcessor{TPixel}"/> class.
/// </summary>
public TritanopiaProcessor()
: base(MatrixFilters.TritanopiaFilter)
{
M11 = 0.95F,
M21 = 0.05F,
M22 = 0.433F,
M23 = 0.475F,
M32 = 0.567F,
M33 = 0.525F,
M44 = 1
};
/// <inheritdoc/>
public override bool Compand => false;
}
}
}

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

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

0
src/ImageSharp/Processing/Processors/ColorMatrix/FilterProcessor.cs → src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs

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

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

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

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

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

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

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

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

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

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

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

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary>
/// <param name="options">The options effecting blending and composition.</param>
public LomographProcessor(GraphicsOptions options)
: base(Matrix4x4Extensions.LomographFilter)
: base(MatrixFilters.LomographFilter)
{
this.options = options;
}

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

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

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

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary>
/// <param name="options">The options effecting blending and composition.</param>
public PolaroidProcessor(GraphicsOptions options)
: base(Matrix4x4Extensions.PolaroidFilter)
: base(MatrixFilters.PolaroidFilter)
{
this.options = options;
}

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

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

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

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

8
tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs → tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs

@ -1,12 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class BlackWhiteTest : BaseImageOperationsExtensionTest
{
@ -14,14 +12,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
public void BlackWhite_CorrectProcessor()
{
this.operations.BlackWhite();
var p = this.Verify<BlackWhiteProcessor<Rgba32>>();
BlackWhiteProcessor<Rgba32> p = this.Verify<BlackWhiteProcessor<Rgba32>>();
}
[Fact]
public void BlackWhite_rect_CorrectProcessor()
{
this.operations.BlackWhite( this.rect);
var p = this.Verify<BlackWhiteProcessor<Rgba32>>(this.rect);
BlackWhiteProcessor<Rgba32> p = this.Verify<BlackWhiteProcessor<Rgba32>>(this.rect);
}
}
}

0
tests/ImageSharp.Tests/Processing/ColorMatrix/BrightnessTest.cs → tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs

2
tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs → tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs

@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class ColorBlindnessTest : BaseImageOperationsExtensionTest
{

0
tests/ImageSharp.Tests/Processing/ColorMatrix/ContrastTest.cs → tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs

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

@ -0,0 +1,26 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class FilterTest : BaseImageOperationsExtensionTest
{
[Fact]
public void Filter_CorrectProcessor()
{
this.operations.Filter(MatrixFilters.AchromatomalyFilter * MatrixFilters.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);
FilterProcessor<Rgba32> p = this.Verify<FilterProcessor<Rgba32>>(this.rect);
}
}
}

2
tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs → tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs

@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class GrayscaleTest : BaseImageOperationsExtensionTest
{

2
tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs → tests/ImageSharp.Tests/Processing/Filters/HueTest.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class HueTest : BaseImageOperationsExtensionTest
{

0
tests/ImageSharp.Tests/Processing/ColorMatrix/InvertTest.cs → tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs

2
tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs → tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class KodachromeTest : BaseImageOperationsExtensionTest
{

0
tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs → tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs

0
tests/ImageSharp.Tests/Processing/ColorMatrix/OpacityTest.cs → tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs

2
tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs → tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class PolaroidTest : BaseImageOperationsExtensionTest
{

2
tests/ImageSharp.Tests/Processing/ColorMatrix/SaturateTest.cs → tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs

@ -4,7 +4,7 @@
using SixLabors.ImageSharp.Processing.Processors;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class SaturateTest : BaseImageOperationsExtensionTest
{

2
tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs → tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Filters
{
public class SepiaTest : BaseImageOperationsExtensionTest
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class BlackWhiteTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs

@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class ColorBlindnessTest : FileTestBase
{

44
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/FilterTest.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class FilterTest : FileTestBase
{
[Theory]
[WithFileCollection(nameof(DefaultFiles), DefaultPixelType)]
public void ImageShouldApplyFilter<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Filter(MatrixFilters.CreateBrightnessFilter(1.2F) * MatrixFilters.CreateContrastFilter(1.2F)));
image.DebugSave(provider);
}
}
[Theory]
[WithFileCollection(nameof(DefaultFiles), DefaultPixelType)]
public void ImageShouldApplyFilterInBox<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = source.Clone())
{
var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2);
image.Mutate(x => x.Filter(MatrixFilters.CreateBrightnessFilter(1.2F) * MatrixFilters.CreateContrastFilter(1.2F), bounds));
image.DebugSave(provider);
ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds);
}
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs

@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class GrayscaleTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class HueTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class KodachromeTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class LomographTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class PolaroidTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturateTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class SaturateTest : FileTestBase
{

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs

@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
{
public class SepiaTest : FileTestBase
{

Loading…
Cancel
Save