Browse Source

Fix build, simplify utilities

pull/2894/head
James Jackson-South 1 year ago
parent
commit
32da5cdd82
  1. 5
      Directory.Build.props
  2. 2
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  3. 52
      src/ImageSharp/Formats/EncodingUtilities.cs
  4. 8
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  5. 4
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  6. 4
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  7. 4
      src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs
  8. 4
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  9. 4
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  10. 2
      src/ImageSharp/Formats/TransparentColorMode.cs
  11. 6
      src/ImageSharp/IDeepCloneable.cs
  12. 21
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs
  13. 1
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
  14. 43
      src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs
  15. 49
      src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
  16. 13
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

5
Directory.Build.props

@ -21,9 +21,8 @@
<!-- Import the shared global .props file -->
<Import Project="$(MSBuildThisFileDirectory)shared-infrastructure\msbuild\props\SixLabors.Global.props" />
<PropertyGroup Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<!-- Workaround various issues bound to implicit language features. -->
<LangVersion>preview</LangVersion>
<PropertyGroup>
<LangVersion>13.0</LangVersion>
</PropertyGroup>
<!--

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

@ -368,7 +368,7 @@ internal sealed class BmpEncoderCore
if (bpp > 8 && EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(this.transparentColorMode))
{
clonedFrame = image.Frames.RootFrame.Clone();
EncodingUtilities.ReplaceTransparentPixels(clonedFrame, Color.Transparent);
EncodingUtilities.ReplaceTransparentPixels(clonedFrame);
}
ImageFrame<TPixel> encodingFrame = clonedFrame ?? image.Frames.RootFrame;

52
src/ImageSharp/Formats/EncodingUtilities.cs

@ -27,71 +27,58 @@ internal static class EncodingUtilities
=> mode == TransparentColorMode.Clear && TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated;
/// <summary>
/// Replaces transparent pixels with pixels represented by <paramref name="color"/>.
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="frame">The <see cref="ImageFrame{TPixel}"/> where the transparent pixels will be changed.</param>
/// <param name="color">The color to replace transparent pixels with.</param>
public static void ReplaceTransparentPixels<TPixel>(ImageFrame<TPixel> frame, Color color)
public static void ReplaceTransparentPixels<TPixel>(ImageFrame<TPixel> frame)
where TPixel : unmanaged, IPixel<TPixel>
=> ReplaceTransparentPixels(frame.Configuration, frame.PixelBuffer, color);
=> ReplaceTransparentPixels(frame.Configuration, frame.PixelBuffer);
/// <summary>
/// Replaces transparent pixels with pixels represented by <paramref name="color"/>.
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="buffer">The <see cref="Buffer2D{TPixel}"/> where the transparent pixels will be changed.</param>
/// <param name="color">The color to replace transparent pixels with.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReplaceTransparentPixels<TPixel>(
Configuration configuration,
Buffer2D<TPixel> buffer,
Color color)
public static void ReplaceTransparentPixels<TPixel>(Configuration configuration, Buffer2D<TPixel> buffer)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2DRegion<TPixel> region = buffer.GetRegion();
ReplaceTransparentPixels(configuration, in region, color);
ReplaceTransparentPixels(configuration, in region);
}
/// <summary>
/// Replaces transparent pixels with pixels represented by <paramref name="color"/>.
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="region">The <see cref="Buffer2DRegion{T}"/> where the transparent pixels will be changed.</param>
/// <param name="color">The color to replace transparent pixels with.</param>
public static void ReplaceTransparentPixels<TPixel>(
Configuration configuration,
in Buffer2DRegion<TPixel> region,
Color color)
in Buffer2DRegion<TPixel> region)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<Vector4> vectors = configuration.MemoryAllocator.Allocate<Vector4>(region.Width);
Span<Vector4> vectorsSpan = vectors.GetSpan();
Vector4 replacement = color.ToScaledVector4();
for (int y = 0; y < region.Height; y++)
{
Span<TPixel> span = region.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, span, vectorsSpan, PixelConversionModifiers.Scale);
ReplaceTransparentPixels(vectorsSpan, replacement);
ReplaceTransparentPixels(vectorsSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorsSpan, span, PixelConversionModifiers.Scale);
}
}
/// <summary>
/// Replaces transparent pixels with pixels represented by <paramref name="replacement"/>.
/// Replaces pixels with a transparent alpha component with fully transparent pixels.
/// </summary>
/// <param name="source">A span of color vectors that will be checked for transparency and potentially modified.</param>
/// <param name="replacement">A color vector that will replace transparent pixels when the alpha value is below the specified threshold.</param>
public static void ReplaceTransparentPixels(Span<Vector4> source, Vector4 replacement)
public static void ReplaceTransparentPixels(Span<Vector4> source)
{
if (Vector512.IsHardwareAccelerated && source.Length >= 4)
{
Vector128<float> replacement128 = replacement.AsVector128();
Vector256<float> replacement256 = Vector256.Create(replacement128, replacement128);
Vector512<float> replacement512 = Vector512.Create(replacement256, replacement256);
Span<Vector512<float>> source512 = MemoryMarshal.Cast<Vector4, Vector512<float>>(source);
for (int i = 0; i < source512.Length; i++)
{
@ -105,7 +92,7 @@ internal static class EncodingUtilities
// Use the mask to select the replacement vector
// (replacement & mask) | (v512 & ~mask)
v = Vector512.ConditionalSelect(mask, replacement512, v);
v = Vector512.ConditionalSelect(mask, Vector512<float>.Zero, v);
}
int m = Numerics.Modulo4(source.Length);
@ -115,16 +102,13 @@ internal static class EncodingUtilities
{
if (source[i].W == 0)
{
source[i] = replacement;
source[i] = Vector4.Zero;
}
}
}
}
else if (Vector256.IsHardwareAccelerated && source.Length >= 2)
{
Vector128<float> replacement128 = replacement.AsVector128();
Vector256<float> replacement256 = Vector256.Create(replacement128, replacement128);
Span<Vector256<float>> source256 = MemoryMarshal.Cast<Vector4, Vector256<float>>(source);
for (int i = 0; i < source256.Length; i++)
{
@ -138,7 +122,7 @@ internal static class EncodingUtilities
// Use the mask to select the replacement vector
// (replacement & mask) | (v256 & ~mask)
v = Vector256.ConditionalSelect(mask, replacement256, v);
v = Vector256.ConditionalSelect(mask, Vector256<float>.Zero, v);
}
int m = Numerics.Modulo2(source.Length);
@ -148,15 +132,13 @@ internal static class EncodingUtilities
{
if (source[i].W == 0)
{
source[i] = replacement;
source[i] = Vector4.Zero;
}
}
}
}
else if (Vector128.IsHardwareAccelerated)
{
Vector128<float> replacement128 = replacement.AsVector128();
for (int i = 0; i < source.Length; i++)
{
ref Vector4 v = ref source[i];
@ -170,7 +152,7 @@ internal static class EncodingUtilities
// Use the mask to select the replacement vector
// (replacement & mask) | (v128 & ~mask)
v = Vector128.ConditionalSelect(mask, replacement128, v128).AsVector4();
v = Vector128.ConditionalSelect(mask, Vector128<float>.Zero, v128).AsVector4();
}
}
else
@ -179,7 +161,7 @@ internal static class EncodingUtilities
{
if (source[i].W == 0F)
{
source[i] = replacement;
source[i] = Vector4.Zero;
}
}
}

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

@ -146,12 +146,16 @@ internal sealed class GifEncoderCore
}
}
// Create a new quantizer options instance augmenting the transparent color mode to match the encoder.
TransparentColorMode mode = this.transparentColorMode;
QuantizerOptions options = this.quantizer.Options.DeepClone(o => o.TransparentColorMode = mode);
// Quantize the first frame. Checking to see whether we can clear the transparent pixels
// to allow for a smaller color palette and encoded result.
// TODO: What should we use as the background color here?
Color background = Color.Transparent;
using (IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration))
using (IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, options))
{
TransparentColorMode mode = this.transparentColorMode;
IPixelSamplingStrategy strategy = this.pixelSamplingStrategy;
ImageFrame<TPixel> encodingFrame = image.Frames.RootFrame;

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

@ -1086,7 +1086,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
{
PixelBlender<TPixel> blender =
PixelOperations<TPixel>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver);
blender.Blend(this.configuration, destination, destination, rowSpan, 1f);
blender.Blend<TPixel>(this.configuration, destination, destination, rowSpan, 1F);
}
}
finally
@ -1208,7 +1208,7 @@ internal sealed class PngDecoderCore : ImageDecoderCore
{
PixelBlender<TPixel> blender =
PixelOperations<TPixel>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver);
blender.Blend(this.configuration, destination, destination, rowSpan, 1F);
blender.Blend<TPixel>(this.configuration, destination, destination, rowSpan, 1F);
}
}
finally

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

@ -197,7 +197,7 @@ internal sealed class PngEncoderCore : IDisposable
{
currentFrame = clonedFrame = currentFrame.Clone();
currentFrameRegion = currentFrame.PixelBuffer.GetRegion();
EncodingUtilities.ReplaceTransparentPixels(this.configuration, in currentFrameRegion, this.backgroundColor.Value);
EncodingUtilities.ReplaceTransparentPixels(this.configuration, in currentFrameRegion);
}
// Do not move this. We require an accurate bit depth for the header chunk.
@ -316,7 +316,7 @@ internal sealed class PngEncoderCore : IDisposable
if (clearTransparency && this.colorType is not PngColorType.Palette)
{
EncodingUtilities.ReplaceTransparentPixels(encodingFrame, background);
EncodingUtilities.ReplaceTransparentPixels(encodingFrame);
}
// Each frame control sequence number must be incremented by the number of frame data chunks that follow.

4
src/ImageSharp/Formats/Qoi/QoiEncoderCore.cs

@ -90,10 +90,12 @@ internal class QoiEncoderCore
ImageFrame<TPixel>? clonedFrame = null;
try
{
// TODO: Try to avoid cloning the frame if possible.
// We should be cloning individual scanlines instead.
if (EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(this.encoder.TransparentColorMode))
{
clonedFrame = image.Frames.RootFrame.Clone();
EncodingUtilities.ReplaceTransparentPixels(clonedFrame, Color.Transparent);
EncodingUtilities.ReplaceTransparentPixels(clonedFrame);
}
ImageFrame<TPixel> encodingFrame = clonedFrame ?? image.Frames.RootFrame;

4
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -110,10 +110,12 @@ internal sealed class TgaEncoderCore
ImageFrame<TPixel>? clonedFrame = null;
try
{
// TODO: Try to avoid cloning the frame if possible.
// We should be cloning individual scanlines instead.
if (EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(this.transparentColorMode))
{
clonedFrame = image.Frames.RootFrame.Clone();
EncodingUtilities.ReplaceTransparentPixels(clonedFrame, Color.Transparent);
EncodingUtilities.ReplaceTransparentPixels(clonedFrame);
}
ImageFrame<TPixel> encodingFrame = clonedFrame ?? image.Frames.RootFrame;

4
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -146,10 +146,12 @@ internal sealed class TiffEncoderCore
{
cancellationToken.ThrowIfCancellationRequested();
// TODO: Try to avoid cloning the frame if possible.
// We should be cloning individual scanlines instead.
if (EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(this.transparentColorMode))
{
clonedFrame = frame.Clone();
EncodingUtilities.ReplaceTransparentPixels(clonedFrame, Color.Transparent);
EncodingUtilities.ReplaceTransparentPixels(clonedFrame);
}
ImageFrame<TPixel> encodingFrame = clonedFrame ?? frame;

2
src/ImageSharp/Formats/TransparentColorMode.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Specifies how transparent pixels should be handled during encoding.
/// Specifies how transparent pixels should be handled during encoding and quantization.
/// </summary>
public enum TransparentColorMode
{

6
src/ImageSharp/IDeepCloneable.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp;
@ -14,7 +14,7 @@ public interface IDeepCloneable<out T>
/// Creates a new <typeparamref name="T"/> that is a deep copy of the current instance.
/// </summary>
/// <returns>The <typeparamref name="T"/>.</returns>
T DeepClone();
public T DeepClone();
}
/// <summary>
@ -26,5 +26,5 @@ public interface IDeepCloneable
/// Creates a new object that is a deep copy of the current instance.
/// </summary>
/// <returns>The <see cref="IDeepCloneable"/>.</returns>
IDeepCloneable DeepClone();
public IDeepCloneable DeepClone();
}

21
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

@ -45,7 +45,7 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
this.maxColors = this.Options.MaxColors;
this.bitDepth = Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.maxColors), 1, 8);
this.octree = new Octree(configuration, this.bitDepth, this.maxColors, this.Options.TransparencyThreshold, this.Options.ThresholdReplacementColor);
this.octree = new Octree(configuration, this.bitDepth, this.maxColors, this.Options.TransparencyThreshold);
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean);
this.pixelMap = default;
this.palette = default;
@ -194,11 +194,6 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
private int previousNode;
private Rgba32 previousColor;
// The color to use for pixels below the transparency threshold.
private Vector4 thresholdReplacementColor;
private Vector4 thresholdReplacementColorV4;
private readonly Rgba32 thresholdReplacementColorRgba;
// Free list for reclaimed node indices.
private readonly Stack<short> freeIndices = new();
@ -209,20 +204,15 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
/// <param name="maxColorBits">The maximum number of significant bits in the image.</param>
/// <param name="maxColors">The maximum number of colors to allow in the palette.</param>
/// <param name="transparencyThreshold">The threshold for transparent colors.</param>
/// <param name="thresholdReplacementColor">The color to use for pixels below the transparency threshold.</param>
public Octree(
Configuration configuration,
int maxColorBits,
int maxColors,
float transparencyThreshold,
Color thresholdReplacementColor)
float transparencyThreshold)
{
this.maxColorBits = maxColorBits;
this.maxColors = maxColors;
this.transparencyThreshold255 = (int)(transparencyThreshold * 255F);
this.thresholdReplacementColor = thresholdReplacementColor.ToScaledVector4();
this.thresholdReplacementColorV4 = this.thresholdReplacementColor * 255F;
this.thresholdReplacementColorRgba = thresholdReplacementColor.ToPixel<Rgba32>();
this.Leaves = 0;
this.previousNode = -1;
this.previousColor = default;
@ -573,7 +563,7 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
if (vector.W < octree.transparencyThreshold255)
{
vector = octree.thresholdReplacementColorV4;
vector = Vector4.Zero;
}
palette[paletteIndex] = TPixel.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W));
@ -602,11 +592,6 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
/// <param name="octree">The parent octree.</param>
public int GetPaletteIndex(Rgba32 color, int level, Octree octree)
{
if (color.A < octree.transparencyThreshold255)
{
color = octree.thresholdReplacementColorRgba;
}
if (this.Leaf)
{
return this.PaletteIndex;

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

@ -98,6 +98,7 @@ internal struct PaletteQuantizer<TPixel> : IQuantizer<TPixel>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly byte GetQuantizedColor(TPixel color, out TPixel match)
{
// TODO: We need to use thesholding here.
if (this.transparencyIndex >= 0 && color.Equals(this.transparentColor))
{
match = this.transparentColor;

43
src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -8,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
/// <summary>
/// Defines options for quantization.
/// </summary>
public class QuantizerOptions
public class QuantizerOptions : IDeepCloneable<QuantizerOptions>
{
#pragma warning disable IDE0032 // Use auto property
private float ditherScale = QuantizerConstants.MaxDitherScale;
@ -16,6 +17,27 @@ public class QuantizerOptions
private float threshold = QuantizerConstants.DefaultTransparencyThreshold;
#pragma warning restore IDE0032 // Use auto property
/// <summary>
/// Initializes a new instance of the <see cref="QuantizerOptions"/> class.
/// </summary>
public QuantizerOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="QuantizerOptions"/> class.
/// </summary>
/// <param name="options">The options to clone.</param>
private QuantizerOptions(QuantizerOptions options)
{
this.Dither = options.Dither;
this.DitherScale = options.DitherScale;
this.MaxColors = options.MaxColors;
this.TransparencyThreshold = options.TransparencyThreshold;
this.ColorMatchingMode = options.ColorMatchingMode;
this.TransparentColorMode = options.TransparentColorMode;
}
/// <summary>
/// Gets or sets the algorithm to apply to the output image.
/// Defaults to <see cref="QuantizerConstants.DefaultDither"/>; set to <see langword="null"/> for no dithering.
@ -42,6 +64,12 @@ public class QuantizerOptions
set => this.maxColors = Numerics.Clamp(value, QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
}
/// <summary>
/// Gets or sets the color matching mode used for matching pixel values to palette colors.
/// Defaults to <see cref="ColorMatchingMode.Coarse"/>.
/// </summary>
public ColorMatchingMode ColorMatchingMode { get; set; } = ColorMatchingMode.Coarse;
/// <summary>
/// Gets or sets the threshold at which to consider a pixel transparent. Range 0..1.
/// Defaults to <see cref="QuantizerConstants.DefaultTransparencyThreshold"/>.
@ -53,14 +81,11 @@ public class QuantizerOptions
}
/// <summary>
/// Gets or sets the color used for replacing colors with an alpha component below the threshold.
/// Defaults to <see cref="Color.Transparent"/>.
/// Gets or sets the transparent color mode used for handling transparent colors.
/// Defaults to <see cref="TransparentColorMode.Preserve"/>.
/// </summary>
public Color ThresholdReplacementColor { get; set; } = Color.Transparent;
public TransparentColorMode TransparentColorMode { get; set; } = TransparentColorMode.Preserve;
/// <summary>
/// Gets or sets the color matching mode used for matching pixel values to palette colors.
/// Defaults to <see cref="ColorMatchingMode.Coarse"/>.
/// </summary>
public ColorMatchingMode ColorMatchingMode { get; set; } = ColorMatchingMode.Coarse;
/// <inheritdoc/>
public QuantizerOptions DeepClone() => new(this);
}

49
src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs

@ -18,6 +18,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
/// </summary>
public static class QuantizerUtilities
{
internal static QuantizerOptions DeepClone(this QuantizerOptions options, Action<QuantizerOptions>? mutate)
{
QuantizerOptions clone = options.DeepClone();
mutate?.Invoke(clone);
return clone;
}
/// <summary>
/// Determines if transparent pixels can be replaced based on the specified color mode and pixel type.
/// </summary>
@ -29,20 +36,15 @@ public static class QuantizerUtilities
=> threshold > 0 && TPixel.GetPixelTypeInfo().AlphaRepresentation == PixelAlphaRepresentation.Unassociated;
/// <summary>
/// Replaces transparent pixels in a span with a specified color based on an alpha threshold.
/// Replaces pixels in a span with fully transparent pixels based on an alpha threshold.
/// </summary>
/// <param name="source">A span of color vectors that will be checked for transparency and potentially modified.</param>
/// <param name="replacement">A color vector that will replace transparent pixels when the alpha value is below the specified threshold.</param>
/// <param name="threshold">The alpha threshold used to determine if a pixel is transparent.</param>
public static void ReplacePixelsByAlphaThreshold(Span<Vector4> source, Vector4 replacement, float threshold)
public static void ReplacePixelsByAlphaThreshold(Span<Vector4> source, float threshold)
{
if (Vector512.IsHardwareAccelerated && source.Length >= 4)
{
Vector128<float> replacement128 = replacement.AsVector128();
Vector256<float> replacement256 = Vector256.Create(replacement128, replacement128);
Vector512<float> replacement512 = Vector512.Create(replacement256, replacement256);
Vector512<float> threshold512 = Vector512.Create(threshold);
Span<Vector512<float>> source512 = MemoryMarshal.Cast<Vector4, Vector512<float>>(source);
for (int i = 0; i < source512.Length; i++)
{
@ -56,7 +58,7 @@ public static class QuantizerUtilities
// Use the mask to select the replacement vector
// (replacement & mask) | (v512 & ~mask)
v = Vector512.ConditionalSelect(mask, replacement512, v);
v = Vector512.ConditionalSelect(mask, Vector512<float>.Zero, v);
}
int m = Numerics.Modulo4(source.Length);
@ -66,17 +68,14 @@ public static class QuantizerUtilities
{
if (source[i].W < threshold)
{
source[i] = replacement;
source[i] = Vector4.Zero;
}
}
}
}
else if (Vector256.IsHardwareAccelerated && source.Length >= 2)
{
Vector128<float> replacement128 = replacement.AsVector128();
Vector256<float> replacement256 = Vector256.Create(replacement128, replacement128);
Vector256<float> threshold256 = Vector256.Create(threshold);
Span<Vector256<float>> source256 = MemoryMarshal.Cast<Vector4, Vector256<float>>(source);
for (int i = 0; i < source256.Length; i++)
{
@ -90,7 +89,7 @@ public static class QuantizerUtilities
// Use the mask to select the replacement vector
// (replacement & mask) | (v256 & ~mask)
v = Vector256.ConditionalSelect(mask, replacement256, v);
v = Vector256.ConditionalSelect(mask, Vector256<float>.Zero, v);
}
int m = Numerics.Modulo2(source.Length);
@ -100,14 +99,13 @@ public static class QuantizerUtilities
{
if (source[i].W < threshold)
{
source[i] = replacement;
source[i] = Vector4.Zero;
}
}
}
}
else if (Vector128.IsHardwareAccelerated)
{
Vector128<float> replacement128 = replacement.AsVector128();
Vector128<float> threshold128 = Vector128.Create(threshold);
for (int i = 0; i < source.Length; i++)
@ -123,7 +121,7 @@ public static class QuantizerUtilities
// Use the mask to select the replacement vector
// (replacement & mask) | (v128 & ~mask)
v = Vector128.ConditionalSelect(mask, replacement128, v128).AsVector4();
v = Vector128.ConditionalSelect(mask, Vector128<float>.Zero, v128).AsVector4();
}
}
else
@ -132,7 +130,7 @@ public static class QuantizerUtilities
{
if (source[i].W < threshold)
{
source[i] = replacement;
source[i] = Vector4.Zero;
}
}
}
@ -336,7 +334,7 @@ public static class QuantizerUtilities
{
Configuration configuration = quantizer.Configuration;
float threshold = quantizer.Options.TransparencyThreshold;
Color replacement = quantizer.Options.ThresholdReplacementColor;
TransparentColorMode mode = quantizer.Options.TransparentColorMode;
using IMemoryOwner<TPixel2> delegateRowOwner = configuration.MemoryAllocator.Allocate<TPixel2>(source.Width);
Span<TPixel2> delegateRow = delegateRowOwner.Memory.Span;
@ -348,7 +346,6 @@ public static class QuantizerUtilities
{
using IMemoryOwner<Vector4> vectorRowOwner = configuration.MemoryAllocator.Allocate<Vector4>(source.Width);
Span<Vector4> vectorRow = vectorRowOwner.Memory.Span;
Vector4 replacementV4 = replacement.ToScaledVector4();
if (replaceByThreshold)
{
@ -357,7 +354,7 @@ public static class QuantizerUtilities
Span<TPixel> sourceRow = source.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale);
ReplacePixelsByAlphaThreshold(vectorRow, replacementV4, threshold);
ReplacePixelsByAlphaThreshold(vectorRow, threshold);
PixelOperations<TPixel2>.Instance.FromVector4Destructive(configuration, vectorRow, delegateRow, PixelConversionModifiers.Scale);
rowDelegate.Invoke(delegateRow, y);
@ -370,7 +367,7 @@ public static class QuantizerUtilities
Span<TPixel> sourceRow = source.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale);
EncodingUtilities.ReplaceTransparentPixels(vectorRow, replacementV4);
EncodingUtilities.ReplaceTransparentPixels(vectorRow);
PixelOperations<TPixel2>.Instance.FromVector4Destructive(configuration, vectorRow, delegateRow, PixelConversionModifiers.Scale);
rowDelegate.Invoke(delegateRow, y);
@ -398,10 +395,8 @@ public static class QuantizerUtilities
where TPixel : unmanaged, IPixel<TPixel>
{
float threshold = quantizer.Options.TransparencyThreshold;
Color replacement = quantizer.Options.ThresholdReplacementColor;
bool replaceByThreshold = ShouldReplacePixelsByAlphaThreshold<TPixel>(threshold);
bool replaceTransparent = EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(mode);
Vector4 replacementV4 = replacement.ToScaledVector4();
IDither? dither = quantizer.Options.Dither;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;
@ -426,7 +421,7 @@ public static class QuantizerUtilities
Span<TPixel> sourceRow = region.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale);
ReplacePixelsByAlphaThreshold(vectorRow, replacementV4, threshold);
ReplacePixelsByAlphaThreshold(vectorRow, threshold);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorRow, quantizingRow, PixelConversionModifiers.Scale);
@ -444,7 +439,7 @@ public static class QuantizerUtilities
Span<TPixel> sourceRow = region.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale);
EncodingUtilities.ReplaceTransparentPixels(vectorRow, replacementV4);
EncodingUtilities.ReplaceTransparentPixels(vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorRow, quantizingRow, PixelConversionModifiers.Scale);
@ -483,7 +478,7 @@ public static class QuantizerUtilities
Span<TPixel> sourceRow = region.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale);
ReplacePixelsByAlphaThreshold(vectorRow, replacementV4, threshold);
ReplacePixelsByAlphaThreshold(vectorRow, threshold);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorRow, sourceRow, PixelConversionModifiers.Scale);
}
@ -495,7 +490,7 @@ public static class QuantizerUtilities
Span<TPixel> sourceRow = region.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, vectorRow, PixelConversionModifiers.Scale);
EncodingUtilities.ReplaceTransparentPixels(vectorRow, replacementV4);
EncodingUtilities.ReplaceTransparentPixels(vectorRow);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorRow, sourceRow, PixelConversionModifiers.Scale);
}

13
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

@ -58,9 +58,6 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
private readonly Box[] colorCube;
private PixelMap<TPixel>? pixelMap;
private readonly bool isDithering;
private readonly int transparencyThreshold255;
private Vector4 thresholdReplacementColorV4;
private readonly Rgba32 thresholdReplacementColorRgba;
private bool isDisposed;
/// <summary>
@ -86,9 +83,6 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
this.pixelMap = default;
this.palette = default;
this.isDithering = this.Options.Dither is not null;
this.transparencyThreshold255 = (int)(this.Options.TransparencyThreshold * 255F);
this.thresholdReplacementColorV4 = this.Options.ThresholdReplacementColor.ToScaledVector4();
this.thresholdReplacementColorRgba = this.Options.ThresholdReplacementColor.ToPixel<Rgba32>();
}
/// <inheritdoc/>
@ -139,7 +133,6 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
ReadOnlySpan<Moment> momentsSpan = this.momentsOwner.GetSpan();
float transparencyThreshold = this.Options.TransparencyThreshold;
Vector4 thresholdReplacementColor = this.thresholdReplacementColorV4;
for (int k = 0; k < paletteSpan.Length; k++)
{
this.Mark(ref this.colorCube[k], (byte)k);
@ -149,7 +142,7 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
Vector4 normalized = moment.Normalize();
if (normalized.W < transparencyThreshold)
{
normalized = thresholdReplacementColor;
normalized = Vector4.Zero;
}
paletteSpan[k] = TPixel.FromScaledVector4(normalized);
@ -188,10 +181,6 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
}
Rgba32 rgba = color.ToRgba32();
if (rgba.A < this.transparencyThreshold255)
{
rgba = this.thresholdReplacementColorRgba;
}
const int shift = 8 - IndexBits;
int r = rgba.R >> shift;

Loading…
Cancel
Save