mirror of https://github.com/SixLabors/ImageSharp
258 changed files with 7894 additions and 8442 deletions
@ -0,0 +1,20 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Bmp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Configuration options for decoding Windows Bitmap images.
|
||||
|
/// </summary>
|
||||
|
public sealed class BmpDecoderOptions : ISpecializedDecoderOptions |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public DecoderOptions GeneralOptions { get; set; } = new(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the value indicating how to deal with skipped pixels,
|
||||
|
/// which can occur during decoding run length encoded bitmaps.
|
||||
|
/// </summary>
|
||||
|
public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -1,16 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Bmp |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Image decoder options for decoding Windows bitmap streams.
|
|
||||
/// </summary>
|
|
||||
internal interface IBmpDecoderOptions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps.
|
|
||||
/// </summary>
|
|
||||
RleSkippedPixelHandling RleSkippedPixelHandling { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,49 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Transforms; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides general configuration options for decoding image formats.
|
||||
|
/// </summary>
|
||||
|
public sealed class DecoderOptions |
||||
|
{ |
||||
|
private static readonly Lazy<DecoderOptions> LazyOptions = new(() => new()); |
||||
|
|
||||
|
private uint maxFrames = int.MaxValue; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the shared default general decoder options instance.
|
||||
|
/// </summary>
|
||||
|
internal static DecoderOptions Default { get; } = LazyOptions.Value; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a custom Configuration instance to be used by the image processing pipeline.
|
||||
|
/// </summary>
|
||||
|
public Configuration Configuration { get; set; } = Configuration.Default; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the target size to decode the image into.
|
||||
|
/// </summary>
|
||||
|
public Size? TargetSize { get; set; } = null; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the sampler to use when resizing during decoding.
|
||||
|
/// </summary>
|
||||
|
public IResampler Sampler { get; set; } = KnownResamplers.Box; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a value indicating whether to ignore encoded metadata when decoding.
|
||||
|
/// </summary>
|
||||
|
public bool SkipMetadata { get; set; } = false; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the maximum number of image frames to decode, inclusive.
|
||||
|
/// </summary>
|
||||
|
public uint MaxFrames { get => this.maxFrames; set => this.maxFrames = Math.Clamp(value, 1, int.MaxValue); } |
||||
|
} |
||||
|
} |
||||
@ -1,23 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using SixLabors.ImageSharp.Metadata; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Gif |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Decoder for generating an image out of a gif encoded stream.
|
|
||||
/// </summary>
|
|
||||
internal interface IGifDecoderOptions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
|
|
||||
/// </summary>
|
|
||||
bool IgnoreMetadata { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the decoding mode for multi-frame images.
|
|
||||
/// </summary>
|
|
||||
FrameDecodingMode DecodingMode { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,45 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.IO; |
||||
|
using System.Threading; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The base class for all specialized image decoders.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of specialized options.</typeparam>
|
||||
|
public interface IImageDecoderSpecialized<T> : IImageDecoder |
||||
|
where T : ISpecializedDecoderOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
|
||||
|
/// </remarks>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="options">The specialized decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public Image<TPixel> Decode<TPixel>(T options, Stream stream, CancellationToken cancellationToken) |
||||
|
where TPixel : unmanaged, IPixel<TPixel>; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This method is designed to support the ImageSharp internal infrastructure and is not recommended for direct use.
|
||||
|
/// </remarks>
|
||||
|
/// <param name="options">The specialized decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public Image Decode(T options, Stream stream, CancellationToken cancellationToken); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides specialized configuration options for decoding image formats.
|
||||
|
/// </summary>
|
||||
|
public interface ISpecializedDecoderOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the general decoder options.
|
||||
|
/// </summary>
|
||||
|
DecoderOptions GeneralOptions { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,182 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.IO; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Extensions methods for <see cref="IImageDecoder"/> and <see cref="IImageDecoderSpecialized{T}"/>.
|
||||
|
/// </summary>
|
||||
|
public static class ImageDecoderExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Reads the raw image information from the specified stream.
|
||||
|
/// </summary>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The general decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <returns>The <see cref="IImageInfo"/> object.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static IImageInfo Identify(this IImageDecoder decoder, DecoderOptions options, Stream stream) |
||||
|
=> Image.WithSeekableStream( |
||||
|
options, |
||||
|
stream, |
||||
|
s => decoder.Identify(options, s, default)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads the raw image information from the specified stream.
|
||||
|
/// </summary>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The general decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>The <see cref="Task{IImageInfo}"/> object.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Task<IImageInfo> IdentifyAsync(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) |
||||
|
=> Image.WithSeekableStreamAsync( |
||||
|
options, |
||||
|
stream, |
||||
|
(s, ct) => decoder.Identify(options, s, ct), |
||||
|
cancellationToken); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The general decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Image<TPixel> Decode<TPixel>(this IImageDecoder decoder, DecoderOptions options, Stream stream) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> Image.WithSeekableStream( |
||||
|
options, |
||||
|
stream, |
||||
|
s => decoder.Decode<TPixel>(options, s, default)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The general decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Image Decode(this IImageDecoder decoder, DecoderOptions options, Stream stream) |
||||
|
=> Image.WithSeekableStream( |
||||
|
options, |
||||
|
stream, |
||||
|
s => decoder.Decode(options, s, default)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The general decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Task<Image<TPixel>> DecodeAsync<TPixel>(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> Image.WithSeekableStreamAsync( |
||||
|
options, |
||||
|
stream, |
||||
|
(s, ct) => decoder.Decode<TPixel>(options, s, ct), |
||||
|
cancellationToken); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The general decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Task<Image> DecodeAsync(this IImageDecoder decoder, DecoderOptions options, Stream stream, CancellationToken cancellationToken = default) |
||||
|
=> Image.WithSeekableStreamAsync( |
||||
|
options, |
||||
|
stream, |
||||
|
(s, ct) => decoder.Decode(options, s, ct), |
||||
|
cancellationToken); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of specialized options.</typeparam>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The specialized decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Image<TPixel> Decode<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream) |
||||
|
where T : ISpecializedDecoderOptions |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> Image.WithSeekableStream( |
||||
|
options.GeneralOptions, |
||||
|
stream, |
||||
|
s => decoder.Decode<TPixel>(options, s, default)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of specialized options.</typeparam>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The specialized decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Image Decode<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream) |
||||
|
where T : ISpecializedDecoderOptions |
||||
|
=> Image.WithSeekableStream( |
||||
|
options.GeneralOptions, |
||||
|
stream, |
||||
|
s => decoder.Decode(options, s, default)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image{TPixel}"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of specialized options.</typeparam>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The specialized decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Task<Image<TPixel>> DecodeAsync<T, TPixel>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default) |
||||
|
where T : ISpecializedDecoderOptions |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> Image.WithSeekableStreamAsync( |
||||
|
options.GeneralOptions, |
||||
|
stream, |
||||
|
(s, ct) => decoder.Decode<TPixel>(options, s, ct), |
||||
|
cancellationToken); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes the image from the specified stream to an <see cref="Image"/> of a specific pixel type.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of specialized options.</typeparam>
|
||||
|
/// <param name="decoder">The decoder.</param>
|
||||
|
/// <param name="options">The specialized decoder options.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
|
||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
|
/// <returns>A <see cref="Task{Image}"/> representing the asynchronous operation.</returns>
|
||||
|
/// <exception cref="ImageFormatException">Thrown if the encoded image contains errors.</exception>
|
||||
|
public static Task<Image> DecodeAsync<T>(this IImageDecoderSpecialized<T> decoder, T options, Stream stream, CancellationToken cancellationToken = default) |
||||
|
where T : ISpecializedDecoderOptions |
||||
|
=> Image.WithSeekableStreamAsync( |
||||
|
options.GeneralOptions, |
||||
|
stream, |
||||
|
(s, ct) => decoder.Decode(options, s, ct), |
||||
|
cancellationToken); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
// ReSharper disable UseObjectOrCollectionInitializer
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal partial struct Block8x8F |
||||
|
{ |
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void ScaledCopyFrom(ref float areaOrigin, int areaStride) => |
||||
|
CopyFrom1x1Scale(ref Unsafe.As<float, byte>(ref areaOrigin), ref Unsafe.As<Block8x8F, byte>(ref this), areaStride); |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
||||
|
{ |
||||
|
if (horizontalScale == 1 && verticalScale == 1) |
||||
|
{ |
||||
|
CopyTo1x1Scale(ref Unsafe.As<Block8x8F, byte>(ref this), ref Unsafe.As<float, byte>(ref areaOrigin), areaStride); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (horizontalScale == 2 && verticalScale == 2) |
||||
|
{ |
||||
|
this.CopyTo2x2Scale(ref areaOrigin, areaStride); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// TODO: Optimize: implement all cases with scale-specific, loopless code!
|
||||
|
this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); |
||||
|
} |
||||
|
|
||||
|
private void CopyTo2x2Scale(ref float areaOrigin, int areaStride) |
||||
|
{ |
||||
|
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin); |
||||
|
int destStride = (int)((uint)areaStride / 2); |
||||
|
|
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 0, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 1, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 2, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 3, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 4, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 5, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 6, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 7, destStride); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
static void WidenCopyRowImpl2x2(ref Vector4 selfBase, ref Vector2 destBase, nint row, nint destStride) |
||||
|
{ |
||||
|
ref Vector4 sLeft = ref Unsafe.Add(ref selfBase, 2 * row); |
||||
|
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
||||
|
|
||||
|
nint offset = 2 * row * destStride; |
||||
|
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
||||
|
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
||||
|
|
||||
|
var xyLeft = new Vector4(sLeft.X); |
||||
|
xyLeft.Z = sLeft.Y; |
||||
|
xyLeft.W = sLeft.Y; |
||||
|
|
||||
|
var zwLeft = new Vector4(sLeft.Z); |
||||
|
zwLeft.Z = sLeft.W; |
||||
|
zwLeft.W = sLeft.W; |
||||
|
|
||||
|
var xyRight = new Vector4(sRight.X); |
||||
|
xyRight.Z = sRight.Y; |
||||
|
xyRight.W = sRight.Y; |
||||
|
|
||||
|
var zwRight = new Vector4(sRight.Z); |
||||
|
zwRight.Z = sRight.W; |
||||
|
zwRight.W = sRight.W; |
||||
|
|
||||
|
dTopLeft = xyLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
||||
|
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
||||
|
|
||||
|
dBottomLeft = xyLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
||||
|
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ColdPath)] |
||||
|
private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
||||
|
{ |
||||
|
for (int y = 0; y < 8; y++) |
||||
|
{ |
||||
|
int yy = y * verticalScale; |
||||
|
int y8 = y * 8; |
||||
|
|
||||
|
for (int x = 0; x < 8; x++) |
||||
|
{ |
||||
|
int xx = x * horizontalScale; |
||||
|
|
||||
|
float value = this[y8 + x]; |
||||
|
nint baseIdx = (yy * areaStride) + xx; |
||||
|
|
||||
|
for (nint i = 0; i < verticalScale; i++, baseIdx += areaStride) |
||||
|
{ |
||||
|
for (nint j = 0; j < horizontalScale; j++) |
||||
|
{ |
||||
|
// area[xx + j, yy + i] = value;
|
||||
|
Unsafe.Add(ref areaOrigin, baseIdx + j) = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void CopyTo1x1Scale(ref byte origin, ref byte dest, int areaStride) |
||||
|
{ |
||||
|
int destStride = areaStride * sizeof(float); |
||||
|
|
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 0); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 1); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 2); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 3); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 4); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 5); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 6); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 7); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
static void CopyRowImpl(ref byte origin, ref byte dest, int destStride, int row) |
||||
|
{ |
||||
|
origin = ref Unsafe.Add(ref origin, row * 8 * sizeof(float)); |
||||
|
dest = ref Unsafe.Add(ref dest, row * destStride); |
||||
|
Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void CopyFrom1x1Scale(ref byte origin, ref byte dest, int areaStride) |
||||
|
{ |
||||
|
int destStride = areaStride * sizeof(float); |
||||
|
|
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 0); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 1); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 2); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 3); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 4); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 5); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 6); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 7); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
static void CopyRowImpl(ref byte origin, ref byte dest, int sourceStride, int row) |
||||
|
{ |
||||
|
origin = ref Unsafe.Add(ref origin, row * sourceStride); |
||||
|
dest = ref Unsafe.Add(ref dest, row * 8 * sizeof(float)); |
||||
|
Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,147 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
// ReSharper disable UseObjectOrCollectionInitializer
|
|
||||
// ReSharper disable InconsistentNaming
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|
||||
{ |
|
||||
internal partial struct Block8x8F |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void ScaledCopyTo(in Buffer2DRegion<float> region, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
ref float areaOrigin = ref region.GetReferenceToOrigin(); |
|
||||
this.ScaledCopyTo(ref areaOrigin, region.Stride, horizontalScale, verticalScale); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
if (horizontalScale == 1 && verticalScale == 1) |
|
||||
{ |
|
||||
this.Copy1x1Scale(ref areaOrigin, areaStride); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (horizontalScale == 2 && verticalScale == 2) |
|
||||
{ |
|
||||
this.Copy2x2Scale(ref areaOrigin, areaStride); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// TODO: Optimize: implement all cases with scale-specific, loopless code!
|
|
||||
this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); |
|
||||
} |
|
||||
|
|
||||
public void Copy1x1Scale(ref float areaOrigin, int areaStride) |
|
||||
{ |
|
||||
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); |
|
||||
ref byte destBase = ref Unsafe.As<float, byte>(ref areaOrigin); |
|
||||
int destStride = areaStride * sizeof(float); |
|
||||
|
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 0); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 1); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 2); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 3); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 4); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 5); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 6); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 7); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) |
|
||||
{ |
|
||||
ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); |
|
||||
ref byte d = ref Unsafe.Add(ref destBase, row * destStride); |
|
||||
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); |
|
||||
} |
|
||||
|
|
||||
private void Copy2x2Scale(ref float areaOrigin, int areaStride) |
|
||||
{ |
|
||||
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin); |
|
||||
int destStride = areaStride / 2; |
|
||||
|
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 2, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 3, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 4, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 5, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 6, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 7, destStride); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride) |
|
||||
{ |
|
||||
ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row); |
|
||||
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
|
||||
|
|
||||
int offset = 2 * row * destStride; |
|
||||
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
|
||||
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
|
||||
|
|
||||
var xyLeft = new Vector4(sLeft.X); |
|
||||
xyLeft.Z = sLeft.Y; |
|
||||
xyLeft.W = sLeft.Y; |
|
||||
|
|
||||
var zwLeft = new Vector4(sLeft.Z); |
|
||||
zwLeft.Z = sLeft.W; |
|
||||
zwLeft.W = sLeft.W; |
|
||||
|
|
||||
var xyRight = new Vector4(sRight.X); |
|
||||
xyRight.Z = sRight.Y; |
|
||||
xyRight.W = sRight.Y; |
|
||||
|
|
||||
var zwRight = new Vector4(sRight.Z); |
|
||||
zwRight.Z = sRight.W; |
|
||||
zwRight.W = sRight.W; |
|
||||
|
|
||||
dTopLeft = xyLeft; |
|
||||
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
|
||||
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
|
||||
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
|
||||
|
|
||||
dBottomLeft = xyLeft; |
|
||||
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
|
||||
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
|
||||
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
|
||||
private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
for (int y = 0; y < 8; y++) |
|
||||
{ |
|
||||
int yy = y * verticalScale; |
|
||||
int y8 = y * 8; |
|
||||
|
|
||||
for (int x = 0; x < 8; x++) |
|
||||
{ |
|
||||
int xx = x * horizontalScale; |
|
||||
|
|
||||
float value = this[y8 + x]; |
|
||||
|
|
||||
for (int i = 0; i < verticalScale; i++) |
|
||||
{ |
|
||||
int baseIdx = ((yy + i) * areaStride) + xx; |
|
||||
|
|
||||
for (int j = 0; j < horizontalScale; j++) |
|
||||
{ |
|
||||
// area[xx + j, yy + i] = value;
|
|
||||
Unsafe.Add(ref areaOrigin, baseIdx + j) = value; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,99 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class CmykAvx : JpegColorConverterAvx |
||||
|
{ |
||||
|
public CmykAvx(int precision) |
||||
|
: base(JpegColorSpace.Cmyk, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector256<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> c1Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> c2Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector256<float> c3Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i); |
||||
|
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i); |
||||
|
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i); |
||||
|
Vector256<float> k = Unsafe.Add(ref c3Base, i); |
||||
|
|
||||
|
k = Avx.Multiply(k, scale); |
||||
|
c = Avx.Multiply(c, k); |
||||
|
m = Avx.Multiply(m, k); |
||||
|
y = Avx.Multiply(y, k); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); |
||||
|
|
||||
|
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
ref Vector256<float> destC = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> destM = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> destY = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector256<float> destK = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
ref Vector256<float> srcR = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane)); |
||||
|
ref Vector256<float> srcG = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane)); |
||||
|
ref Vector256<float> srcB = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane)); |
||||
|
|
||||
|
var scale = Vector256.Create(maxValue); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector256<float> ctmp = Avx.Subtract(scale, Unsafe.Add(ref srcR, i)); |
||||
|
Vector256<float> mtmp = Avx.Subtract(scale, Unsafe.Add(ref srcG, i)); |
||||
|
Vector256<float> ytmp = Avx.Subtract(scale, Unsafe.Add(ref srcB, i)); |
||||
|
Vector256<float> ktmp = Avx.Min(ctmp, Avx.Min(mtmp, ytmp)); |
||||
|
|
||||
|
Vector256<float> kMask = Avx.CompareNotEqual(ktmp, scale); |
||||
|
|
||||
|
ctmp = Avx.And(Avx.Divide(Avx.Subtract(ctmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); |
||||
|
mtmp = Avx.And(Avx.Divide(Avx.Subtract(mtmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); |
||||
|
ytmp = Avx.And(Avx.Divide(Avx.Subtract(ytmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); |
||||
|
|
||||
|
Unsafe.Add(ref destC, i) = Avx.Subtract(scale, Avx.Multiply(ctmp, scale)); |
||||
|
Unsafe.Add(ref destM, i) = Avx.Subtract(scale, Avx.Multiply(mtmp, scale)); |
||||
|
Unsafe.Add(ref destY, i) = Avx.Subtract(scale, Avx.Multiply(ytmp, scale)); |
||||
|
Unsafe.Add(ref destK, i) = Avx.Subtract(scale, ktmp); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -0,0 +1,82 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class CmykScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
public CmykScalar(int precision) |
||||
|
: base(JpegColorSpace.Cmyk, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) => |
||||
|
ConvertToRgbInplace(values, this.MaximumValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertFromRgb(values, this.MaximumValue, r, g, b); |
||||
|
|
||||
|
public static void ConvertToRgbInplace(in ComponentValues values, float maxValue) |
||||
|
{ |
||||
|
Span<float> c0 = values.Component0; |
||||
|
Span<float> c1 = values.Component1; |
||||
|
Span<float> c2 = values.Component2; |
||||
|
Span<float> c3 = values.Component3; |
||||
|
|
||||
|
float scale = 1 / (maxValue * maxValue); |
||||
|
for (int i = 0; i < c0.Length; i++) |
||||
|
{ |
||||
|
float c = c0[i]; |
||||
|
float m = c1[i]; |
||||
|
float y = c2[i]; |
||||
|
float k = c3[i]; |
||||
|
|
||||
|
k *= scale; |
||||
|
c0[i] = c * k; |
||||
|
c1[i] = m * k; |
||||
|
c2[i] = y * k; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b) |
||||
|
{ |
||||
|
Span<float> c = values.Component0; |
||||
|
Span<float> m = values.Component1; |
||||
|
Span<float> y = values.Component2; |
||||
|
Span<float> k = values.Component3; |
||||
|
|
||||
|
for (int i = 0; i < c.Length; i++) |
||||
|
{ |
||||
|
float ctmp = 255f - r[i]; |
||||
|
float mtmp = 255f - g[i]; |
||||
|
float ytmp = 255f - b[i]; |
||||
|
float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp); |
||||
|
|
||||
|
if (ktmp >= 255f) |
||||
|
{ |
||||
|
ctmp = 0f; |
||||
|
mtmp = 0f; |
||||
|
ytmp = 0f; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ctmp = (ctmp - ktmp) / (255f - ktmp); |
||||
|
mtmp = (mtmp - ktmp) / (255f - ktmp); |
||||
|
ytmp = (ytmp - ktmp) / (255f - ktmp); |
||||
|
} |
||||
|
|
||||
|
c[i] = maxValue - (ctmp * maxValue); |
||||
|
m[i] = maxValue - (mtmp * maxValue); |
||||
|
y[i] = maxValue - (ytmp * maxValue); |
||||
|
k[i] = maxValue - ktmp; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class CmykVector : JpegColorConverterVector |
||||
|
{ |
||||
|
public CmykVector(int precision) |
||||
|
: base(JpegColorSpace.Cmyk, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector<float> cBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> mBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> yBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector<float> kBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector<float> c = ref Unsafe.Add(ref cBase, i); |
||||
|
ref Vector<float> m = ref Unsafe.Add(ref mBase, i); |
||||
|
ref Vector<float> y = ref Unsafe.Add(ref yBase, i); |
||||
|
Vector<float> k = Unsafe.Add(ref kBase, i); |
||||
|
|
||||
|
k *= scale; |
||||
|
c *= k; |
||||
|
m *= k; |
||||
|
y *= k; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
||||
|
=> CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, r, g, b); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertFromRgbInplaceRemainder(values, this.MaximumValue, r, g, b); |
||||
|
|
||||
|
public static void ConvertFromRgbInplaceVectorized(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b) |
||||
|
{ |
||||
|
ref Vector<float> destC = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> destM = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> destY = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector<float> destK = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
ref Vector<float> srcR = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(r)); |
||||
|
ref Vector<float> srcG = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(g)); |
||||
|
ref Vector<float> srcB = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(b)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var scale = new Vector<float>(maxValue); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
||||
|
Vector<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
||||
|
Vector<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
||||
|
Vector<float> ktmp = Vector.Min(ctmp, Vector.Min(mtmp, ytmp)); |
||||
|
|
||||
|
var kMask = Vector.Equals(ktmp, scale); |
||||
|
ctmp = Vector.AndNot((ctmp - ktmp) / (scale - ktmp), kMask.As<int, float>()); |
||||
|
mtmp = Vector.AndNot((mtmp - ktmp) / (scale - ktmp), kMask.As<int, float>()); |
||||
|
ytmp = Vector.AndNot((ytmp - ktmp) / (scale - ktmp), kMask.As<int, float>()); |
||||
|
|
||||
|
Unsafe.Add(ref destC, i) = scale - (ctmp * scale); |
||||
|
Unsafe.Add(ref destM, i) = scale - (mtmp * scale); |
||||
|
Unsafe.Add(ref destY, i) = scale - (ytmp * scale); |
||||
|
Unsafe.Add(ref destK, i) = scale - ktmp; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void ConvertFromRgbInplaceRemainder(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> CmykScalar.ConvertFromRgb(values, maxValue, r, g, b); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
using static SixLabors.ImageSharp.SimdUtils; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class GrayscaleAvx : JpegColorConverterAvx |
||||
|
{ |
||||
|
public GrayscaleAvx(int precision) |
||||
|
: base(JpegColorSpace.Grayscale, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector256<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var scale = Vector256.Create(1 / this.MaximumValue); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
||||
|
c0 = Avx.Multiply(c0, scale); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
ref Vector256<float> destLuminance = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
|
||||
|
ref Vector256<float> srcRed = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane)); |
||||
|
ref Vector256<float> srcGreen = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane)); |
||||
|
ref Vector256<float> srcBlue = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var f0299 = Vector256.Create(0.299f); |
||||
|
var f0587 = Vector256.Create(0.587f); |
||||
|
var f0114 = Vector256.Create(0.114f); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector256<float> r = ref Unsafe.Add(ref srcRed, i); |
||||
|
ref Vector256<float> g = ref Unsafe.Add(ref srcGreen, i); |
||||
|
ref Vector256<float> b = ref Unsafe.Add(ref srcBlue, i); |
||||
|
|
||||
|
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -0,0 +1,51 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class GrayscaleScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
public GrayscaleScalar(int precision) |
||||
|
: base(JpegColorSpace.Grayscale, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
=> ConvertToRgbInplace(values.Component0, this.MaximumValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertCoreInplaceFromRgb(values, r, g, b); |
||||
|
|
||||
|
internal static void ConvertToRgbInplace(Span<float> values, float maxValue) |
||||
|
{ |
||||
|
ref float valuesRef = ref MemoryMarshal.GetReference(values); |
||||
|
float scale = 1 / maxValue; |
||||
|
|
||||
|
for (nint i = 0; i < values.Length; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref valuesRef, i) *= scale; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static void ConvertCoreInplaceFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
Span<float> c0 = values.Component0; |
||||
|
|
||||
|
for (int i = 0; i < c0.Length; i++) |
||||
|
{ |
||||
|
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
float luma = (0.299f * rLane[i]) + (0.587f * gLane[i]) + (0.114f * bLane[i]); |
||||
|
c0[i] = luma; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class GrayScaleVector : JpegColorConverterVector |
||||
|
{ |
||||
|
public GrayScaleVector(int precision) |
||||
|
: base(JpegColorSpace.Grayscale, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector<float> cBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
|
||||
|
var scale = new Vector<float>(1 / this.MaximumValue); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector<float> c0 = ref Unsafe.Add(ref cBase, i); |
||||
|
c0 *= scale; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
||||
|
=> GrayscaleScalar.ConvertToRgbInplace(values.Component0, this.MaximumValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
ref Vector<float> destLuma = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
|
||||
|
ref Vector<float> srcR = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(rLane)); |
||||
|
ref Vector<float> srcG = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(gLane)); |
||||
|
ref Vector<float> srcB = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane)); |
||||
|
|
||||
|
var rMult = new Vector<float>(0.299f); |
||||
|
var gMult = new Vector<float>(0.587f); |
||||
|
var bMult = new Vector<float>(0.114f); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector<float> r = Unsafe.Add(ref srcR, i); |
||||
|
Vector<float> g = Unsafe.Add(ref srcR, i); |
||||
|
Vector<float> b = Unsafe.Add(ref srcR, i); |
||||
|
|
||||
|
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
Unsafe.Add(ref destLuma, i) = (rMult * r) + (gMult * g) + (bMult * b); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> GrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class RgbScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
public RgbScalar(int precision) |
||||
|
: base(JpegColorSpace.RGB, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
=> ConvertToRgbInplace(values, this.MaximumValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertFromRgb(values, r, g, b); |
||||
|
|
||||
|
internal static void ConvertToRgbInplace(ComponentValues values, float maxValue) |
||||
|
{ |
||||
|
GrayscaleScalar.ConvertToRgbInplace(values.Component0, maxValue); |
||||
|
GrayscaleScalar.ConvertToRgbInplace(values.Component1, maxValue); |
||||
|
GrayscaleScalar.ConvertToRgbInplace(values.Component2, maxValue); |
||||
|
} |
||||
|
|
||||
|
internal static void ConvertFromRgb(ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
{ |
||||
|
r.CopyTo(values.Component0); |
||||
|
g.CopyTo(values.Component1); |
||||
|
b.CopyTo(values.Component2); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,125 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
using static SixLabors.ImageSharp.SimdUtils; |
||||
|
|
||||
|
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class YCbCrAvx : JpegColorConverterAvx |
||||
|
{ |
||||
|
public YCbCrAvx(int precision) |
||||
|
: base(JpegColorSpace.YCbCr, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector256<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> c1Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> c2Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var chromaOffset = Vector256.Create(-this.HalfValue); |
||||
|
var scale = Vector256.Create(1 / this.MaximumValue); |
||||
|
var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
||||
|
var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
||||
|
var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
||||
|
var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); |
||||
|
|
||||
|
// Walking 8 elements at one step:
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
// y = yVals[i];
|
||||
|
// cb = cbVals[i] - 128F;
|
||||
|
// cr = crVals[i] - 128F;
|
||||
|
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
||||
|
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
||||
|
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
||||
|
|
||||
|
Vector256<float> y = c0; |
||||
|
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
||||
|
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
||||
|
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
||||
|
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
||||
|
|
||||
|
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
||||
|
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
||||
|
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
||||
|
|
||||
|
c0 = r; |
||||
|
c1 = g; |
||||
|
c2 = b; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
ref Vector256<float> destY = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> destCb = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> destCr = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
|
||||
|
ref Vector256<float> srcR = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane)); |
||||
|
ref Vector256<float> srcG = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane)); |
||||
|
ref Vector256<float> srcB = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var chromaOffset = Vector256.Create(this.HalfValue); |
||||
|
|
||||
|
var f0299 = Vector256.Create(0.299f); |
||||
|
var f0587 = Vector256.Create(0.587f); |
||||
|
var f0114 = Vector256.Create(0.114f); |
||||
|
var fn0168736 = Vector256.Create(-0.168736f); |
||||
|
var fn0331264 = Vector256.Create(-0.331264f); |
||||
|
var fn0418688 = Vector256.Create(-0.418688f); |
||||
|
var fn0081312F = Vector256.Create(-0.081312F); |
||||
|
var f05 = Vector256.Create(0.5f); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector256<float> r = Unsafe.Add(ref srcR, i); |
||||
|
Vector256<float> g = Unsafe.Add(ref srcG, i); |
||||
|
Vector256<float> b = Unsafe.Add(ref srcB, i); |
||||
|
|
||||
|
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
||||
|
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
||||
|
Vector256<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
||||
|
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
||||
|
Vector256<float> cr = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
||||
|
|
||||
|
Unsafe.Add(ref destY, i) = y; |
||||
|
Unsafe.Add(ref destCb, i) = cb; |
||||
|
Unsafe.Add(ref destCr, i) = cr; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -0,0 +1,76 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class YCbCrScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
// derived from ITU-T Rec. T.871
|
||||
|
internal const float RCrMult = 1.402f; |
||||
|
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
||||
|
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
||||
|
internal const float BCbMult = 1.772f; |
||||
|
|
||||
|
public YCbCrScalar(int precision) |
||||
|
: base(JpegColorSpace.YCbCr, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
=> ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertFromRgb(values, this.HalfValue, r, g, b); |
||||
|
|
||||
|
public static void ConvertToRgbInplace(in ComponentValues values, float maxValue, float halfValue) |
||||
|
{ |
||||
|
Span<float> c0 = values.Component0; |
||||
|
Span<float> c1 = values.Component1; |
||||
|
Span<float> c2 = values.Component2; |
||||
|
|
||||
|
float scale = 1 / maxValue; |
||||
|
|
||||
|
for (int i = 0; i < c0.Length; i++) |
||||
|
{ |
||||
|
float y = c0[i]; |
||||
|
float cb = c1[i] - halfValue; |
||||
|
float cr = c2[i] - halfValue; |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
c0[i] = MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
||||
|
c1[i] = MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
||||
|
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scale; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void ConvertFromRgb(in ComponentValues values, float halfValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
Span<float> y = values.Component0; |
||||
|
Span<float> cb = values.Component1; |
||||
|
Span<float> cr = values.Component2; |
||||
|
|
||||
|
for (int i = 0; i < y.Length; i++) |
||||
|
{ |
||||
|
float r = rLane[i]; |
||||
|
float g = gLane[i]; |
||||
|
float b = bLane[i]; |
||||
|
|
||||
|
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
||||
|
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
||||
|
y[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); |
||||
|
cb[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); |
||||
|
cr[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class YCbCrVector : JpegColorConverterVector |
||||
|
{ |
||||
|
public YCbCrVector(int precision) |
||||
|
: base(JpegColorSpace.YCbCr, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> c1Base = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> c2Base = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
|
||||
|
var chromaOffset = new Vector<float>(-this.HalfValue); |
||||
|
|
||||
|
var scale = new Vector<float>(1 / this.MaximumValue); |
||||
|
var rCrMult = new Vector<float>(YCbCrScalar.RCrMult); |
||||
|
var gCbMult = new Vector<float>(-YCbCrScalar.GCbMult); |
||||
|
var gCrMult = new Vector<float>(-YCbCrScalar.GCrMult); |
||||
|
var bCbMult = new Vector<float>(YCbCrScalar.BCbMult); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
// y = yVals[i];
|
||||
|
// cb = cbVals[i] - 128F;
|
||||
|
// cr = crVals[i] - 128F;
|
||||
|
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
||||
|
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
||||
|
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
||||
|
Vector<float> y = Unsafe.Add(ref c0Base, i); |
||||
|
Vector<float> cb = Unsafe.Add(ref c1Base, i) + chromaOffset; |
||||
|
Vector<float> cr = Unsafe.Add(ref c2Base, i) + chromaOffset; |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
Vector<float> r = y + (cr * rCrMult); |
||||
|
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
||||
|
Vector<float> b = y + (cb * bCbMult); |
||||
|
|
||||
|
r = r.FastRound(); |
||||
|
g = g.FastRound(); |
||||
|
b = b.FastRound(); |
||||
|
r *= scale; |
||||
|
g *= scale; |
||||
|
b *= scale; |
||||
|
|
||||
|
c0 = r; |
||||
|
c1 = g; |
||||
|
c2 = b; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
||||
|
=> YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
ref Vector<float> destY = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> destCb = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> destCr = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
|
||||
|
ref Vector<float> srcR = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(rLane)); |
||||
|
ref Vector<float> srcG = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(gLane)); |
||||
|
ref Vector<float> srcB = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane)); |
||||
|
|
||||
|
var chromaOffset = new Vector<float>(this.HalfValue); |
||||
|
|
||||
|
var rYMult = new Vector<float>(0.299f); |
||||
|
var gYMult = new Vector<float>(0.587f); |
||||
|
var bYMult = new Vector<float>(0.114f); |
||||
|
|
||||
|
var rCbMult = new Vector<float>(0.168736f); |
||||
|
var gCbMult = new Vector<float>(0.331264f); |
||||
|
var bCbMult = new Vector<float>(0.5f); |
||||
|
|
||||
|
var rCrMult = new Vector<float>(0.5f); |
||||
|
var gCrMult = new Vector<float>(0.418688f); |
||||
|
var bCrMult = new Vector<float>(0.081312f); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector<float> r = Unsafe.Add(ref srcR, i); |
||||
|
Vector<float> g = Unsafe.Add(ref srcG, i); |
||||
|
Vector<float> b = Unsafe.Add(ref srcB, i); |
||||
|
|
||||
|
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
||||
|
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
||||
|
Unsafe.Add(ref destY, i) = (rYMult * r) + (gYMult * g) + (bYMult * b); |
||||
|
Unsafe.Add(ref destCb, i) = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b); |
||||
|
Unsafe.Add(ref destCr, i) = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> YCbCrScalar.ConvertFromRgb(values, this.HalfValue, r, g, b); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
using static SixLabors.ImageSharp.SimdUtils; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class YccKAvx : JpegColorConverterAvx |
||||
|
{ |
||||
|
public YccKAvx(int precision) |
||||
|
: base(JpegColorSpace.Ycck, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector256<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> c1Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> c2Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector256<float> kBase = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var chromaOffset = Vector256.Create(-this.HalfValue); |
||||
|
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
||||
|
var max = Vector256.Create(this.MaximumValue); |
||||
|
var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
||||
|
var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
||||
|
var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
||||
|
var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); |
||||
|
|
||||
|
// Walking 8 elements at one step:
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
// y = yVals[i];
|
||||
|
// cb = cbVals[i] - 128F;
|
||||
|
// cr = crVals[i] - 128F;
|
||||
|
// k = kVals[i] / 256F;
|
||||
|
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
||||
|
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
||||
|
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
||||
|
Vector256<float> y = c0; |
||||
|
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
||||
|
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
||||
|
Vector256<float> scaledK = Avx.Multiply(Unsafe.Add(ref kBase, i), scale); |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
||||
|
Vector256<float> g = |
||||
|
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
||||
|
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
||||
|
|
||||
|
r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); |
||||
|
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); |
||||
|
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); |
||||
|
|
||||
|
r = Avx.Multiply(r, scaledK); |
||||
|
g = Avx.Multiply(g, scaledK); |
||||
|
b = Avx.Multiply(b, scaledK); |
||||
|
|
||||
|
c0 = r; |
||||
|
c1 = g; |
||||
|
c2 = b; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
// rgb -> cmyk
|
||||
|
CmykAvx.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); |
||||
|
|
||||
|
// cmyk -> ycck
|
||||
|
ref Vector256<float> destY = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> destCb = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> destCr = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
|
||||
|
ref Vector256<float> srcR = ref destY; |
||||
|
ref Vector256<float> srcG = ref destCb; |
||||
|
ref Vector256<float> srcB = ref destCr; |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var maxSampleValue = Vector256.Create(this.MaximumValue); |
||||
|
|
||||
|
var chromaOffset = Vector256.Create(this.HalfValue); |
||||
|
|
||||
|
var f0299 = Vector256.Create(0.299f); |
||||
|
var f0587 = Vector256.Create(0.587f); |
||||
|
var f0114 = Vector256.Create(0.114f); |
||||
|
var fn0168736 = Vector256.Create(-0.168736f); |
||||
|
var fn0331264 = Vector256.Create(-0.331264f); |
||||
|
var fn0418688 = Vector256.Create(-0.418688f); |
||||
|
var fn0081312F = Vector256.Create(-0.081312F); |
||||
|
var f05 = Vector256.Create(0.5f); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector256<float> r = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i)); |
||||
|
Vector256<float> g = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i)); |
||||
|
Vector256<float> b = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcB, i)); |
||||
|
|
||||
|
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
||||
|
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
||||
|
Vector256<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
||||
|
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
||||
|
Vector256<float> cr = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
||||
|
|
||||
|
Unsafe.Add(ref destY, i) = y; |
||||
|
Unsafe.Add(ref destCb, i) = cb; |
||||
|
Unsafe.Add(ref destCr, i) = cr; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -0,0 +1,80 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class YccKScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
// derived from ITU-T Rec. T.871
|
||||
|
internal const float RCrMult = 1.402f; |
||||
|
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
||||
|
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
||||
|
internal const float BCbMult = 1.772f; |
||||
|
|
||||
|
public YccKScalar(int precision) |
||||
|
: base(JpegColorSpace.Ycck, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
=> ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
=> ConvertFromRgb(values, this.HalfValue, this.MaximumValue, r, g, b); |
||||
|
|
||||
|
public static void ConvertToRgpInplace(in ComponentValues values, float maxValue, float halfValue) |
||||
|
{ |
||||
|
Span<float> c0 = values.Component0; |
||||
|
Span<float> c1 = values.Component1; |
||||
|
Span<float> c2 = values.Component2; |
||||
|
Span<float> c3 = values.Component3; |
||||
|
|
||||
|
float scale = 1 / (maxValue * maxValue); |
||||
|
|
||||
|
for (int i = 0; i < values.Component0.Length; i++) |
||||
|
{ |
||||
|
float y = c0[i]; |
||||
|
float cb = c1[i] - halfValue; |
||||
|
float cr = c2[i] - halfValue; |
||||
|
float scaledK = c3[i] * scale; |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
c0[i] = (maxValue - MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero)) * scaledK; |
||||
|
c1[i] = (maxValue - MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero)) * scaledK; |
||||
|
c2[i] = (maxValue - MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero)) * scaledK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void ConvertFromRgb(in ComponentValues values, float halfValue, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
// rgb -> cmyk
|
||||
|
CmykScalar.ConvertFromRgb(in values, maxValue, rLane, gLane, bLane); |
||||
|
|
||||
|
// cmyk -> ycck
|
||||
|
Span<float> c = values.Component0; |
||||
|
Span<float> m = values.Component1; |
||||
|
Span<float> y = values.Component2; |
||||
|
|
||||
|
for (int i = 0; i < y.Length; i++) |
||||
|
{ |
||||
|
float r = maxValue - c[i]; |
||||
|
float g = maxValue - m[i]; |
||||
|
float b = maxValue - y[i]; |
||||
|
|
||||
|
// k value is passed untouched from rgb -> cmyk conversion
|
||||
|
c[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); |
||||
|
m[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); |
||||
|
y[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,138 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class YccKVector : JpegColorConverterVector |
||||
|
{ |
||||
|
public YccKVector(int precision) |
||||
|
: base(JpegColorSpace.Ycck, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> c1Base = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> c2Base = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector<float> kBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
var chromaOffset = new Vector<float>(-this.HalfValue); |
||||
|
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
||||
|
var max = new Vector<float>(this.MaximumValue); |
||||
|
var rCrMult = new Vector<float>(YCbCrScalar.RCrMult); |
||||
|
var gCbMult = new Vector<float>(-YCbCrScalar.GCbMult); |
||||
|
var gCrMult = new Vector<float>(-YCbCrScalar.GCrMult); |
||||
|
var bCbMult = new Vector<float>(YCbCrScalar.BCbMult); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
// y = yVals[i];
|
||||
|
// cb = cbVals[i] - 128F;
|
||||
|
// cr = crVals[i] - 128F;
|
||||
|
// k = kVals[i] / 256F;
|
||||
|
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
||||
|
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
||||
|
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
||||
|
|
||||
|
Vector<float> y = c0; |
||||
|
Vector<float> cb = c1 + chromaOffset; |
||||
|
Vector<float> cr = c2 + chromaOffset; |
||||
|
Vector<float> scaledK = Unsafe.Add(ref kBase, i) * scale; |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
Vector<float> r = y + (cr * rCrMult); |
||||
|
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
||||
|
Vector<float> b = y + (cb * bCbMult); |
||||
|
|
||||
|
r = (max - r.FastRound()) * scaledK; |
||||
|
g = (max - g.FastRound()) * scaledK; |
||||
|
b = (max - b.FastRound()) * scaledK; |
||||
|
|
||||
|
c0 = r; |
||||
|
c1 = g; |
||||
|
c2 = b; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
||||
|
=> YccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
||||
|
{ |
||||
|
// rgb -> cmyk
|
||||
|
CmykVector.ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, rLane, gLane, bLane); |
||||
|
|
||||
|
// cmyk -> ycck
|
||||
|
ref Vector<float> destY = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> destCb = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> destCr = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
|
||||
|
ref Vector<float> srcR = ref destY; |
||||
|
ref Vector<float> srcG = ref destCb; |
||||
|
ref Vector<float> srcB = ref destCr; |
||||
|
|
||||
|
var maxSampleValue = new Vector<float>(this.MaximumValue); |
||||
|
|
||||
|
var chromaOffset = new Vector<float>(this.HalfValue); |
||||
|
|
||||
|
var rYMult = new Vector<float>(0.299f); |
||||
|
var gYMult = new Vector<float>(0.587f); |
||||
|
var bYMult = new Vector<float>(0.114f); |
||||
|
|
||||
|
var rCbMult = new Vector<float>(0.168736f); |
||||
|
var gCbMult = new Vector<float>(0.331264f); |
||||
|
var bCbMult = new Vector<float>(0.5f); |
||||
|
|
||||
|
var rCrMult = new Vector<float>(0.5f); |
||||
|
var gCrMult = new Vector<float>(0.418688f); |
||||
|
var bCrMult = new Vector<float>(0.081312f); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
Vector<float> r = maxSampleValue - Unsafe.Add(ref srcR, i); |
||||
|
Vector<float> g = maxSampleValue - Unsafe.Add(ref srcG, i); |
||||
|
Vector<float> b = maxSampleValue - Unsafe.Add(ref srcB, i); |
||||
|
|
||||
|
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
|
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
||||
|
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
||||
|
Unsafe.Add(ref destY, i) = (rYMult * r) + (gYMult * g) + (bYMult * b); |
||||
|
Unsafe.Add(ref destCb, i) = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b); |
||||
|
Unsafe.Add(ref destCr, i) = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
{ |
||||
|
// rgb -> cmyk
|
||||
|
CmykScalar.ConvertFromRgb(in values, this.MaximumValue, r, g, b); |
||||
|
|
||||
|
// cmyk -> ycck
|
||||
|
YccKScalar.ConvertFromRgb(in values, this.HalfValue, this.MaximumValue, r, g, b); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,7 +1,7 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
{ |
{ |
||||
internal abstract partial class JpegColorConverterBase |
internal abstract partial class JpegColorConverterBase |
||||
{ |
{ |
||||
@ -0,0 +1,131 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
||||
|
/// based on <see cref="Vector"/> API.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Converters of this family can work with data of any size.
|
||||
|
/// Even though real life data is guaranteed to be of size
|
||||
|
/// divisible by 8 newer SIMD instructions like AVX512 won't work with
|
||||
|
/// such data out of the box. These converters have fallback code
|
||||
|
/// for 'remainder' data.
|
||||
|
/// </remarks>
|
||||
|
internal abstract class JpegColorConverterVector : JpegColorConverterBase |
||||
|
{ |
||||
|
protected JpegColorConverterVector(JpegColorSpace colorSpace, int precision) |
||||
|
: base(colorSpace, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether this converter is supported on current hardware.
|
||||
|
/// </summary>
|
||||
|
public static bool IsSupported => Vector.IsHardwareAccelerated && Vector<float>.Count % 4 == 0; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public sealed override bool IsAvailable => IsSupported; |
||||
|
|
||||
|
public override int ElementsPerBatch => Vector<float>.Count; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public sealed override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
||||
|
|
||||
|
int length = values.Component0.Length; |
||||
|
int remainder = (int)((uint)length % (uint)Vector<float>.Count); |
||||
|
|
||||
|
int simdCount = length - remainder; |
||||
|
if (simdCount > 0) |
||||
|
{ |
||||
|
this.ConvertToRgbInplaceVectorized(values.Slice(0, simdCount)); |
||||
|
} |
||||
|
|
||||
|
// Jpeg images width is always divisible by 8 without a remainder
|
||||
|
// so it's safe to say SSE/AVX1/AVX2 implementations would never have
|
||||
|
// 'remainder' pixels
|
||||
|
// But some exotic simd implementations e.g. AVX-512 can have
|
||||
|
// remainder pixels
|
||||
|
if (remainder > 0) |
||||
|
{ |
||||
|
this.ConvertToRgbInplaceScalarRemainder(values.Slice(simdCount, remainder)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public sealed override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
||||
|
|
||||
|
int length = values.Component0.Length; |
||||
|
int remainder = (int)((uint)length % (uint)Vector<float>.Count); |
||||
|
|
||||
|
int simdCount = length - remainder; |
||||
|
if (simdCount > 0) |
||||
|
{ |
||||
|
this.ConvertFromRgbVectorized( |
||||
|
values.Slice(0, simdCount), |
||||
|
r.Slice(0, simdCount), |
||||
|
g.Slice(0, simdCount), |
||||
|
b.Slice(0, simdCount)); |
||||
|
} |
||||
|
|
||||
|
// Jpeg images width is always divisible by 8 without a remainder
|
||||
|
// so it's safe to say SSE/AVX1/AVX2 implementations would never have
|
||||
|
// 'remainder' pixels
|
||||
|
// But some exotic simd implementations e.g. AVX-512 can have
|
||||
|
// remainder pixels
|
||||
|
if (remainder > 0) |
||||
|
{ |
||||
|
this.ConvertFromRgbScalarRemainder( |
||||
|
values.Slice(simdCount, remainder), |
||||
|
r.Slice(simdCount, remainder), |
||||
|
g.Slice(simdCount, remainder), |
||||
|
b.Slice(simdCount, remainder)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts planar jpeg component values in <paramref name="values"/>
|
||||
|
/// to RGB color space inplace using <see cref="Vector"/> API.
|
||||
|
/// </summary>
|
||||
|
/// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param>
|
||||
|
protected abstract void ConvertToRgbInplaceVectorized(in ComponentValues values); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts remainder of the planar jpeg component values after
|
||||
|
/// conversion in <see cref="ConvertToRgbInplaceVectorized(in ComponentValues)"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param>
|
||||
|
protected abstract void ConvertToRgbInplaceScalarRemainder(in ComponentValues values); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts RGB lanes to jpeg component values using <see cref="Vector"/> API.
|
||||
|
/// </summary>
|
||||
|
/// <param name="values">Jpeg component values.</param>
|
||||
|
/// <param name="rLane">Red colors lane.</param>
|
||||
|
/// <param name="gLane">Green colors lane.</param>
|
||||
|
/// <param name="bLane">Blue colors lane.</param>
|
||||
|
protected abstract void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts remainder of RGB lanes to jpeg component values after
|
||||
|
/// conversion in <see cref="ConvertFromRgbVectorized(in ComponentValues, Span{float}, Span{float}, Span{float})"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="values">Jpeg component values.</param>
|
||||
|
/// <param name="rLane">Red colors lane.</param>
|
||||
|
/// <param name="gLane">Green colors lane.</param>
|
||||
|
/// <param name="bLane">Blue colors lane.</param>
|
||||
|
protected abstract void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,50 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromCmykAvx : JpegColorConverterAvx |
|
||||
{ |
|
||||
public FromCmykAvx(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector256<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector256<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector256<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector256<float> c3Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
|
|
||||
nint n = values.Component0.Length / Vector256<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector256<float> k = Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
k = Avx.Multiply(k, scale); |
|
||||
c = Avx.Multiply(c, k); |
|
||||
m = Avx.Multiply(m, k); |
|
||||
y = Avx.Multiply(y, k); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,43 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromCmykScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public FromCmykScalar(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|
||||
ConvertCoreInplace(values, this.MaximumValue); |
|
||||
|
|
||||
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
float scale = 1 / (maxValue * maxValue); |
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
float c = c0[i]; |
|
||||
float m = c1[i]; |
|
||||
float y = c2[i]; |
|
||||
float k = c3[i]; |
|
||||
|
|
||||
k *= scale; |
|
||||
c0[i] = c * k; |
|
||||
c1[i] = m * k; |
|
||||
c2[i] = y * k; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,51 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromCmykVector : JpegColorConverterVector |
|
||||
{ |
|
||||
public FromCmykVector(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector<float> cBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector<float> mBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector<float> yBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector<float> kBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
|
|
||||
nint n = values.Component0.Length / Vector<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector<float> c = ref Unsafe.Add(ref cBase, i); |
|
||||
ref Vector<float> m = ref Unsafe.Add(ref mBase, i); |
|
||||
ref Vector<float> y = ref Unsafe.Add(ref yBase, i); |
|
||||
Vector<float> k = Unsafe.Add(ref kBase, i); |
|
||||
|
|
||||
k *= scale; |
|
||||
c *= k; |
|
||||
m *= k; |
|
||||
y *= k; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromCmykScalar.ConvertCoreInplace(values, this.MaximumValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,37 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromGrayscaleAvx : JpegColorConverterAvx |
|
||||
{ |
|
||||
public FromGrayscaleAvx(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector256<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
var scale = Vector256.Create(1 / this.MaximumValue); |
|
||||
|
|
||||
nint n = values.Component0.Length / Vector256<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
c0 = Avx.Multiply(c0, scale); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,34 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromGrayscaleScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public FromGrayscaleScalar(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|
||||
ConvertCoreInplace(values.Component0, this.MaximumValue); |
|
||||
|
|
||||
internal static void ConvertCoreInplace(Span<float> values, float maxValue) |
|
||||
{ |
|
||||
ref float valuesRef = ref MemoryMarshal.GetReference(values); |
|
||||
float scale = 1 / maxValue; |
|
||||
|
|
||||
for (nint i = 0; i < values.Length; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref valuesRef, i) *= scale; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromGrayScaleVector : JpegColorConverterVector |
|
||||
{ |
|
||||
public FromGrayScaleVector(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector<float> cBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
|
|
||||
var scale = new Vector<float>(1 / this.MaximumValue); |
|
||||
|
|
||||
nint n = values.Component0.Length / Vector<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector<float> c0 = ref Unsafe.Add(ref cBase, i); |
|
||||
c0 *= scale; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, this.MaximumValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,26 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromRgbScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public FromRgbScalar(int precision) |
|
||||
: base(JpegColorSpace.RGB, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|
||||
ConvertCoreInplace(values, this.MaximumValue); |
|
||||
|
|
||||
internal static void ConvertCoreInplace(ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, maxValue); |
|
||||
FromGrayscaleScalar.ConvertCoreInplace(values.Component1, maxValue); |
|
||||
FromGrayscaleScalar.ConvertCoreInplace(values.Component2, maxValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,72 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using static SixLabors.ImageSharp.SimdUtils; |
|
||||
|
|
||||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromYCbCrAvx : JpegColorConverterAvx |
|
||||
{ |
|
||||
public FromYCbCrAvx(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector256<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector256<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector256<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|
||||
var scale = Vector256.Create(1 / this.MaximumValue); |
|
||||
var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult); |
|
||||
var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult); |
|
||||
var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult); |
|
||||
var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult); |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nint n = values.Component0.Length / Vector256<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
// cb = cbVals[i] - 128F;
|
|
||||
// cr = crVals[i] - 128F;
|
|
||||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
|
|
||||
Vector256<float> y = c0; |
|
||||
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
|
||||
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|
||||
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|
||||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|
||||
|
|
||||
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
|
||||
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
|
||||
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,50 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromYCbCrScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
// TODO: comments, derived from ITU-T Rec. T.871
|
|
||||
internal const float RCrMult = 1.402f; |
|
||||
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
|
||||
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
|
||||
internal const float BCbMult = 1.772f; |
|
||||
|
|
||||
public FromYCbCrScalar(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
=> ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
|
|
||||
float scale = 1 / maxValue; |
|
||||
|
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
float y = c0[i]; |
|
||||
float cb = c1[i] - halfValue; |
|
||||
float cr = c2[i] - halfValue; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
c0[i] = MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
|
||||
c1[i] = MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
|
||||
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scale; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromYCbCrVector : JpegColorConverterVector |
|
||||
{ |
|
||||
public FromYCbCrVector(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
|
|
||||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|
||||
|
|
||||
var scale = new Vector<float>(1 / this.MaximumValue); |
|
||||
var rCrMult = new Vector<float>(FromYCbCrScalar.RCrMult); |
|
||||
var gCbMult = new Vector<float>(-FromYCbCrScalar.GCbMult); |
|
||||
var gCrMult = new Vector<float>(-FromYCbCrScalar.GCrMult); |
|
||||
var bCbMult = new Vector<float>(FromYCbCrScalar.BCbMult); |
|
||||
|
|
||||
nint n = values.Component0.Length / Vector<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
// cb = cbVals[i] - 128F;
|
|
||||
// cr = crVals[i] - 128F;
|
|
||||
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector<float> y = Unsafe.Add(ref c0Base, i); |
|
||||
Vector<float> cb = Unsafe.Add(ref c1Base, i) + chromaOffset; |
|
||||
Vector<float> cr = Unsafe.Add(ref c2Base, i) + chromaOffset; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector<float> r = y + (cr * rCrMult); |
|
||||
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
|
||||
Vector<float> b = y + (cb * bCbMult); |
|
||||
|
|
||||
r = r.FastRound(); |
|
||||
g = g.FastRound(); |
|
||||
b = b.FastRound(); |
|
||||
r *= scale; |
|
||||
g *= scale; |
|
||||
b *= scale; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromYCbCrScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,80 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using static SixLabors.ImageSharp.SimdUtils; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromYccKAvx : JpegColorConverterAvx |
|
||||
{ |
|
||||
public FromYccKAvx(int precision) |
|
||||
: base(JpegColorSpace.Ycck, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector256<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector256<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector256<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector256<float> kBase = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|
||||
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
var max = Vector256.Create(this.MaximumValue); |
|
||||
var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult); |
|
||||
var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult); |
|
||||
var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult); |
|
||||
var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult); |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nint n = values.Component0.Length / Vector256<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
// cb = cbVals[i] - 128F;
|
|
||||
// cr = crVals[i] - 128F;
|
|
||||
// k = kVals[i] / 256F;
|
|
||||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector256<float> y = c0; |
|
||||
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
|
||||
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
|
||||
Vector256<float> scaledK = Avx.Multiply(Unsafe.Add(ref kBase, i), scale); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|
||||
Vector256<float> g = |
|
||||
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|
||||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|
||||
|
|
||||
r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); |
|
||||
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); |
|
||||
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); |
|
||||
|
|
||||
r = Avx.Multiply(r, scaledK); |
|
||||
g = Avx.Multiply(g, scaledK); |
|
||||
b = Avx.Multiply(b, scaledK); |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,43 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromYccKScalar : JpegColorConverterScalar |
|
||||
{ |
|
||||
public FromYccKScalar(int precision) |
|
||||
: base(JpegColorSpace.Ycck, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|
||||
ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
Span<float> c3 = values.Component3; |
|
||||
|
|
||||
float scale = 1 / (maxValue * maxValue); |
|
||||
|
|
||||
for (int i = 0; i < values.Component0.Length; i++) |
|
||||
{ |
|
||||
float y = c0[i]; |
|
||||
float cb = c1[i] - halfValue; |
|
||||
float cr = c2[i] - halfValue; |
|
||||
float scaledK = c3[i] * scale; |
|
||||
|
|
||||
c0[i] = (maxValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|
||||
c1[i] = (maxValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|
||||
c2[i] = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * scaledK; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,75 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
internal sealed class FromYccKVector : JpegColorConverterVector |
|
||||
{ |
|
||||
public FromYccKVector(int precision) |
|
||||
: base(JpegColorSpace.Ycck, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector<float> kBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|
||||
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
|
||||
var max = new Vector<float>(this.MaximumValue); |
|
||||
var rCrMult = new Vector<float>(FromYCbCrScalar.RCrMult); |
|
||||
var gCbMult = new Vector<float>(-FromYCbCrScalar.GCbMult); |
|
||||
var gCrMult = new Vector<float>(-FromYCbCrScalar.GCrMult); |
|
||||
var bCbMult = new Vector<float>(FromYCbCrScalar.BCbMult); |
|
||||
|
|
||||
nint n = values.Component0.Length / Vector<float>.Count; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
// cb = cbVals[i] - 128F;
|
|
||||
// cr = crVals[i] - 128F;
|
|
||||
// k = kVals[i] / 256F;
|
|
||||
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
|
|
||||
Vector<float> y = c0; |
|
||||
Vector<float> cb = c1 + chromaOffset; |
|
||||
Vector<float> cr = c2 + chromaOffset; |
|
||||
Vector<float> scaledK = Unsafe.Add(ref kBase, i) * scale; |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector<float> r = y + (cr * rCrMult); |
|
||||
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
|
||||
Vector<float> b = y + (cb * bCbMult); |
|
||||
|
|
||||
r = (max - r.FastRound()) * scaledK; |
|
||||
g = (max - g.FastRound()) * scaledK; |
|
||||
b = (max - b.FastRound()) * scaledK; |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromYccKScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,61 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverterBase |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
|
||||
/// based on <see cref="Vector"/> API.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// Converters of this family can work with data of any size.
|
|
||||
/// Even though real life data is guaranteed to be of size
|
|
||||
/// divisible by 8 newer SIMD instructions like AVX512 won't work with
|
|
||||
/// such data out of the box. These converters have fallback code
|
|
||||
/// for remainder data.
|
|
||||
/// </remarks>
|
|
||||
internal abstract class JpegColorConverterVector : JpegColorConverterBase |
|
||||
{ |
|
||||
protected JpegColorConverterVector(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public sealed override bool IsAvailable => Vector.IsHardwareAccelerated && Vector<float>.Count % 4 == 0; |
|
||||
|
|
||||
public sealed override int ElementsPerBatch => Vector<float>.Count; |
|
||||
|
|
||||
public sealed override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
|
||||
|
|
||||
int length = values.Component0.Length; |
|
||||
int remainder = (int)((uint)length % (uint)Vector<float>.Count); |
|
||||
|
|
||||
// Jpeg images are guaranteed to have pixel strides at least 8 pixels wide
|
|
||||
// Thus there's no need to check whether simdCount is greater than zero
|
|
||||
int simdCount = length - remainder; |
|
||||
this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount)); |
|
||||
|
|
||||
// Jpeg images width is always divisible by 8 without a remainder
|
|
||||
// so it's safe to say SSE/AVX implementations would never have
|
|
||||
// 'remainder' pixels
|
|
||||
// But some exotic simd implementations e.g. AVX-512 can have
|
|
||||
// remainder pixels
|
|
||||
if (remainder > 0) |
|
||||
{ |
|
||||
this.ConvertCoreInplace(values.Slice(simdCount, remainder)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException(); |
|
||||
|
|
||||
protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,116 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a single frame component.
|
||||
|
/// </summary>
|
||||
|
internal class Component : IDisposable |
||||
|
{ |
||||
|
private readonly MemoryAllocator memoryAllocator; |
||||
|
|
||||
|
public Component(MemoryAllocator memoryAllocator, int horizontalFactor, int verticalFactor, int quantizationTableIndex) |
||||
|
{ |
||||
|
this.memoryAllocator = memoryAllocator; |
||||
|
|
||||
|
this.HorizontalSamplingFactor = horizontalFactor; |
||||
|
this.VerticalSamplingFactor = verticalFactor; |
||||
|
this.SamplingFactors = new Size(horizontalFactor, verticalFactor); |
||||
|
|
||||
|
this.QuantizationTableIndex = quantizationTableIndex; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets DC coefficient predictor.
|
||||
|
/// </summary>
|
||||
|
public int DcPredictor { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the horizontal sampling factor.
|
||||
|
/// </summary>
|
||||
|
public int HorizontalSamplingFactor { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the vertical sampling factor.
|
||||
|
/// </summary>
|
||||
|
public int VerticalSamplingFactor { get; } |
||||
|
|
||||
|
public Buffer2D<Block8x8> SpectralBlocks { get; private set; } |
||||
|
|
||||
|
public Size SubSamplingDivisors { get; private set; } |
||||
|
|
||||
|
public int QuantizationTableIndex { get; } |
||||
|
|
||||
|
public Size SizeInBlocks { get; private set; } |
||||
|
|
||||
|
public Size SamplingFactors { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the number of blocks per line.
|
||||
|
/// </summary>
|
||||
|
public int WidthInBlocks { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the number of blocks per column.
|
||||
|
/// </summary>
|
||||
|
public int HeightInBlocks { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the index for the DC Huffman table.
|
||||
|
/// </summary>
|
||||
|
public int DcTableId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the index for the AC Huffman table.
|
||||
|
/// </summary>
|
||||
|
public int AcTableId { get; set; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
this.SpectralBlocks?.Dispose(); |
||||
|
this.SpectralBlocks = null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes component for future buffers initialization.
|
||||
|
/// </summary>
|
||||
|
/// <param name="frame">asdfasdf.</param>
|
||||
|
/// <param name="maxSubFactorH">Maximal horizontal subsampling factor among all the components.</param>
|
||||
|
/// <param name="maxSubFactorV">Maximal vertical subsampling factor among all the components.</param>
|
||||
|
public void Init(JpegFrame frame, int maxSubFactorH, int maxSubFactorV) |
||||
|
{ |
||||
|
uint widthInBlocks = ((uint)frame.PixelWidth + 7) / 8; |
||||
|
uint heightInBlocks = ((uint)frame.PixelHeight + 7) / 8; |
||||
|
|
||||
|
this.WidthInBlocks = (int)MathF.Ceiling( |
||||
|
(float)widthInBlocks * this.HorizontalSamplingFactor / maxSubFactorH); |
||||
|
|
||||
|
this.HeightInBlocks = (int)MathF.Ceiling( |
||||
|
(float)heightInBlocks * this.VerticalSamplingFactor / maxSubFactorV); |
||||
|
|
||||
|
int blocksPerLineForMcu = frame.McusPerLine * this.HorizontalSamplingFactor; |
||||
|
int blocksPerColumnForMcu = frame.McusPerColumn * this.VerticalSamplingFactor; |
||||
|
this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu); |
||||
|
|
||||
|
this.SubSamplingDivisors = new Size(maxSubFactorH, maxSubFactorV).DivideBy(this.SamplingFactors); |
||||
|
|
||||
|
if (this.SubSamplingDivisors.Width == 0 || this.SubSamplingDivisors.Height == 0) |
||||
|
{ |
||||
|
JpegThrowHelper.ThrowBadSampling(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void AllocateSpectral(bool fullScan) |
||||
|
{ |
||||
|
int spectralAllocWidth = this.SizeInBlocks.Width; |
||||
|
int spectralAllocHeight = fullScan ? this.SizeInBlocks.Height : this.VerticalSamplingFactor; |
||||
|
|
||||
|
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(spectralAllocWidth, spectralAllocHeight); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,231 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
internal class ComponentProcessor : IDisposable |
||||
|
{ |
||||
|
private readonly Size blockAreaSize; |
||||
|
|
||||
|
private readonly Component component; |
||||
|
|
||||
|
private Block8x8F quantTable; |
||||
|
|
||||
|
public ComponentProcessor(MemoryAllocator memoryAllocator, Component component, Size postProcessorBufferSize, Block8x8F quantTable) |
||||
|
{ |
||||
|
this.component = component; |
||||
|
this.quantTable = quantTable; |
||||
|
|
||||
|
this.component = component; |
||||
|
this.blockAreaSize = component.SubSamplingDivisors * 8; |
||||
|
|
||||
|
// alignment of 8 so each block stride can be sampled from a single 'ref pointer'
|
||||
|
this.ColorBuffer = memoryAllocator.Allocate2DOveraligned<float>( |
||||
|
postProcessorBufferSize.Width, |
||||
|
postProcessorBufferSize.Height, |
||||
|
8, |
||||
|
AllocationOptions.Clean); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the temporary working buffer of color values.
|
||||
|
/// </summary>
|
||||
|
public Buffer2D<float> ColorBuffer { get; } |
||||
|
|
||||
|
public void CopyColorBufferToBlocks(int spectralStep) |
||||
|
{ |
||||
|
Buffer2D<Block8x8> spectralBuffer = this.component.SpectralBlocks; |
||||
|
int destAreaStride = this.ColorBuffer.Width; |
||||
|
int yBlockStart = spectralStep * this.component.SamplingFactors.Height; |
||||
|
|
||||
|
Block8x8F workspaceBlock = default; |
||||
|
|
||||
|
// handle subsampling
|
||||
|
Size subsamplingFactors = this.component.SubSamplingDivisors; |
||||
|
if (subsamplingFactors.Width != 1 || subsamplingFactors.Height != 1) |
||||
|
{ |
||||
|
this.PackColorBuffer(); |
||||
|
} |
||||
|
|
||||
|
int blocksRowsPerStep = this.component.SamplingFactors.Height; |
||||
|
|
||||
|
for (int y = 0; y < blocksRowsPerStep; y++) |
||||
|
{ |
||||
|
int yBuffer = y * this.blockAreaSize.Height; |
||||
|
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); |
||||
|
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); |
||||
|
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) |
||||
|
{ |
||||
|
// load 8x8 block from 8 pixel strides
|
||||
|
int xColorBufferStart = xBlock * 8; |
||||
|
workspaceBlock.ScaledCopyFrom( |
||||
|
ref colorBufferRow[xColorBufferStart], |
||||
|
destAreaStride); |
||||
|
|
||||
|
// level shift via -128f
|
||||
|
workspaceBlock.AddInPlace(-128f); |
||||
|
|
||||
|
// FDCT
|
||||
|
FloatingPointDCT.TransformFDCT(ref workspaceBlock); |
||||
|
|
||||
|
// Quantize and save to spectral blocks
|
||||
|
Block8x8F.Quantize(ref workspaceBlock, ref blockRow[xBlock], ref this.quantTable); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Span<float> GetColorBufferRowSpan(int row) |
||||
|
=> this.ColorBuffer.DangerousGetRowSpan(row); |
||||
|
|
||||
|
public void Dispose() |
||||
|
=> this.ColorBuffer.Dispose(); |
||||
|
|
||||
|
private void PackColorBuffer() |
||||
|
{ |
||||
|
Size factors = this.component.SubSamplingDivisors; |
||||
|
|
||||
|
int packedWidth = this.ColorBuffer.Width / factors.Width; |
||||
|
|
||||
|
float averageMultiplier = 1f / (factors.Width * factors.Height); |
||||
|
for (int i = 0; i < this.ColorBuffer.Height; i += factors.Height) |
||||
|
{ |
||||
|
Span<float> sourceRow = this.ColorBuffer.DangerousGetRowSpan(i); |
||||
|
|
||||
|
// vertical sum
|
||||
|
for (int j = 1; j < factors.Height; j++) |
||||
|
{ |
||||
|
SumVertical(sourceRow, this.ColorBuffer.DangerousGetRowSpan(i + j)); |
||||
|
} |
||||
|
|
||||
|
// horizontal sum
|
||||
|
SumHorizontal(sourceRow, factors.Width); |
||||
|
|
||||
|
// calculate average
|
||||
|
MultiplyToAverage(sourceRow, averageMultiplier); |
||||
|
|
||||
|
// copy to the first 8 slots
|
||||
|
sourceRow.Slice(0, packedWidth).CopyTo(this.ColorBuffer.DangerousGetRowSpan(i / factors.Height)); |
||||
|
} |
||||
|
|
||||
|
static void SumVertical(Span<float> target, Span<float> source) |
||||
|
{ |
||||
|
if (Avx.IsSupported) |
||||
|
{ |
||||
|
ref Vector256<float> targetVectorRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
||||
|
ref Vector256<float> sourceVectorRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
||||
|
|
||||
|
// Spans are guaranteed to be multiple of 8 so no extra 'remainder' steps are needed
|
||||
|
nint count = source.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref targetVectorRef, i) = Avx.Add(Unsafe.Add(ref targetVectorRef, i), Unsafe.Add(ref sourceVectorRef, i)); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ref Vector<float> targetVectorRef = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(target)); |
||||
|
ref Vector<float> sourceVectorRef = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source)); |
||||
|
|
||||
|
nint count = source.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref targetVectorRef, i) += Unsafe.Add(ref sourceVectorRef, i); |
||||
|
} |
||||
|
|
||||
|
ref float targetRef = ref MemoryMarshal.GetReference(target); |
||||
|
ref float sourceRef = ref MemoryMarshal.GetReference(source); |
||||
|
for (nint i = count * Vector<float>.Count; i < source.Length; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref targetRef, i) += Unsafe.Add(ref sourceRef, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void SumHorizontal(Span<float> target, int factor) |
||||
|
{ |
||||
|
Span<float> source = target; |
||||
|
if (Avx2.IsSupported) |
||||
|
{ |
||||
|
ref Vector256<float> targetRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
||||
|
|
||||
|
// Ideally we need to use log2: Numerics.Log2((uint)factor)
|
||||
|
// but division by 2 works just fine in this case
|
||||
|
int haddIterationsCount = (int)((uint)factor / 2); |
||||
|
|
||||
|
// Transform spans so that it only contains 'remainder'
|
||||
|
// values for the scalar fallback code
|
||||
|
int scalarRemainder = target.Length % (Vector<float>.Count * factor); |
||||
|
int touchedCount = target.Length - scalarRemainder; |
||||
|
source = source.Slice(touchedCount); |
||||
|
target = target.Slice(touchedCount / factor); |
||||
|
|
||||
|
uint length = (uint)touchedCount / (uint)Vector256<float>.Count; |
||||
|
|
||||
|
for (int i = 0; i < haddIterationsCount; i++) |
||||
|
{ |
||||
|
length /= 2; |
||||
|
|
||||
|
for (nuint j = 0; j < length; j++) |
||||
|
{ |
||||
|
nuint indexLeft = j * 2; |
||||
|
nuint indexRight = indexLeft + 1; |
||||
|
Vector256<float> sum = Avx.HorizontalAdd(Unsafe.Add(ref targetRef, indexLeft), Unsafe.Add(ref targetRef, indexRight)); |
||||
|
Unsafe.Add(ref targetRef, j) = Avx2.Permute4x64(sum.AsDouble(), 0b11_01_10_00).AsSingle(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// scalar remainder
|
||||
|
for (int i = 0; i < source.Length / factor; i++) |
||||
|
{ |
||||
|
target[i] = source[i * factor]; |
||||
|
for (int j = 1; j < factor; j++) |
||||
|
{ |
||||
|
target[i] += source[(i * factor) + j]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void MultiplyToAverage(Span<float> target, float multiplier) |
||||
|
{ |
||||
|
if (Avx.IsSupported) |
||||
|
{ |
||||
|
ref Vector256<float> targetVectorRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
||||
|
|
||||
|
// Spans are guaranteed to be multiple of 8 so no extra 'remainder' steps are needed
|
||||
|
nint count = target.Length / Vector256<float>.Count; |
||||
|
var multiplierVector = Vector256.Create(multiplier); |
||||
|
for (nint i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref targetVectorRef, i) = Avx.Multiply(Unsafe.Add(ref targetVectorRef, i), multiplierVector); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ref Vector<float> targetVectorRef = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(target)); |
||||
|
|
||||
|
nint count = target.Length / Vector<float>.Count; |
||||
|
var multiplierVector = new Vector<float>(multiplier); |
||||
|
for (nint i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref targetVectorRef, i) *= multiplierVector; |
||||
|
} |
||||
|
|
||||
|
ref float targetRef = ref MemoryMarshal.GetReference(target); |
||||
|
for (nint i = count * Vector<float>.Count; i < target.Length; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref targetRef, i) *= multiplier; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
internal class JpegComponentConfig |
||||
|
{ |
||||
|
public JpegComponentConfig(byte id, int hsf, int vsf, int quantIndex, int dcIndex, int acIndex) |
||||
|
{ |
||||
|
this.Id = id; |
||||
|
this.HorizontalSampleFactor = hsf; |
||||
|
this.VerticalSampleFactor = vsf; |
||||
|
this.QuantizatioTableIndex = quantIndex; |
||||
|
this.DcTableSelector = dcIndex; |
||||
|
this.AcTableSelector = acIndex; |
||||
|
} |
||||
|
|
||||
|
public byte Id { get; } |
||||
|
|
||||
|
public int HorizontalSampleFactor { get; } |
||||
|
|
||||
|
public int VerticalSampleFactor { get; } |
||||
|
|
||||
|
public int QuantizatioTableIndex { get; } |
||||
|
|
||||
|
public int DcTableSelector { get; } |
||||
|
|
||||
|
public int AcTableSelector { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
internal class JpegFrameConfig |
||||
|
{ |
||||
|
public JpegFrameConfig(JpegColorSpace colorType, JpegEncodingColor encodingColor, JpegComponentConfig[] components, JpegHuffmanTableConfig[] huffmanTables, JpegQuantizationTableConfig[] quantTables) |
||||
|
{ |
||||
|
this.ColorType = colorType; |
||||
|
this.EncodingColor = encodingColor; |
||||
|
this.Components = components; |
||||
|
this.HuffmanTables = huffmanTables; |
||||
|
this.QuantizationTables = quantTables; |
||||
|
|
||||
|
this.MaxHorizontalSamplingFactor = components[0].HorizontalSampleFactor; |
||||
|
this.MaxVerticalSamplingFactor = components[0].VerticalSampleFactor; |
||||
|
for (int i = 1; i < components.Length; i++) |
||||
|
{ |
||||
|
JpegComponentConfig component = components[i]; |
||||
|
this.MaxHorizontalSamplingFactor = Math.Max(this.MaxHorizontalSamplingFactor, component.HorizontalSampleFactor); |
||||
|
this.MaxVerticalSamplingFactor = Math.Max(this.MaxVerticalSamplingFactor, component.VerticalSampleFactor); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public JpegColorSpace ColorType { get; } |
||||
|
|
||||
|
public JpegEncodingColor EncodingColor { get; } |
||||
|
|
||||
|
public JpegComponentConfig[] Components { get; } |
||||
|
|
||||
|
public JpegHuffmanTableConfig[] HuffmanTables { get; } |
||||
|
|
||||
|
public JpegQuantizationTableConfig[] QuantizationTables { get; } |
||||
|
|
||||
|
public int MaxHorizontalSamplingFactor { get; } |
||||
|
|
||||
|
public int MaxVerticalSamplingFactor { get; } |
||||
|
|
||||
|
public byte? AdobeColorTransformMarkerFlag { get; set; } = null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
internal class JpegHuffmanTableConfig |
||||
|
{ |
||||
|
public JpegHuffmanTableConfig(int @class, int destIndex, HuffmanSpec table) |
||||
|
{ |
||||
|
this.Class = @class; |
||||
|
this.DestinationIndex = destIndex; |
||||
|
this.Table = table; |
||||
|
} |
||||
|
|
||||
|
public int Class { get; } |
||||
|
|
||||
|
public int DestinationIndex { get; } |
||||
|
|
||||
|
public HuffmanSpec Table { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
internal class JpegQuantizationTableConfig |
||||
|
{ |
||||
|
public JpegQuantizationTableConfig(int destIndex, ReadOnlySpan<byte> quantizationTable) |
||||
|
{ |
||||
|
this.DestinationIndex = destIndex; |
||||
|
this.Table = Block8x8.Load(quantizationTable); |
||||
|
} |
||||
|
|
||||
|
public int DestinationIndex { get; } |
||||
|
|
||||
|
public Block8x8 Table { get; } |
||||
|
} |
||||
|
} |
||||
@ -1,35 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Enumerates the Huffman tables
|
|
||||
/// </summary>
|
|
||||
internal enum HuffIndex |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The DC luminance huffman table index
|
|
||||
/// </summary>
|
|
||||
LuminanceDC = 0, |
|
||||
|
|
||||
// ReSharper disable UnusedMember.Local
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The AC luminance huffman table index
|
|
||||
/// </summary>
|
|
||||
LuminanceAC = 1, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The DC chrominance huffman table index
|
|
||||
/// </summary>
|
|
||||
ChrominanceDC = 2, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The AC chrominance huffman table index
|
|
||||
/// </summary>
|
|
||||
ChrominanceAC = 3, |
|
||||
|
|
||||
// ReSharper restore UnusedMember.Local
|
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,85 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represent a single jpeg frame.
|
||||
|
/// </summary>
|
||||
|
internal sealed class JpegFrame : IDisposable |
||||
|
{ |
||||
|
public JpegFrame(Image image, JpegFrameConfig frameConfig, bool interleaved) |
||||
|
{ |
||||
|
this.ColorSpace = frameConfig.ColorType; |
||||
|
|
||||
|
this.Interleaved = interleaved; |
||||
|
|
||||
|
this.PixelWidth = image.Width; |
||||
|
this.PixelHeight = image.Height; |
||||
|
|
||||
|
MemoryAllocator allocator = image.GetConfiguration().MemoryAllocator; |
||||
|
|
||||
|
JpegComponentConfig[] componentConfigs = frameConfig.Components; |
||||
|
this.Components = new Component[componentConfigs.Length]; |
||||
|
for (int i = 0; i < this.Components.Length; i++) |
||||
|
{ |
||||
|
JpegComponentConfig componentConfig = componentConfigs[i]; |
||||
|
this.Components[i] = new Component(allocator, componentConfig.HorizontalSampleFactor, componentConfig.VerticalSampleFactor, componentConfig.QuantizatioTableIndex) |
||||
|
{ |
||||
|
DcTableId = componentConfig.DcTableSelector, |
||||
|
AcTableId = componentConfig.AcTableSelector, |
||||
|
}; |
||||
|
|
||||
|
this.BlocksPerMcu += componentConfig.HorizontalSampleFactor * componentConfig.VerticalSampleFactor; |
||||
|
} |
||||
|
|
||||
|
int maxSubFactorH = frameConfig.MaxHorizontalSamplingFactor; |
||||
|
int maxSubFactorV = frameConfig.MaxVerticalSamplingFactor; |
||||
|
this.McusPerLine = (int)Numerics.DivideCeil((uint)image.Width, (uint)maxSubFactorH * 8); |
||||
|
this.McusPerColumn = (int)Numerics.DivideCeil((uint)image.Height, (uint)maxSubFactorV * 8); |
||||
|
|
||||
|
for (int i = 0; i < this.Components.Length; i++) |
||||
|
{ |
||||
|
Component component = this.Components[i]; |
||||
|
component.Init(this, maxSubFactorH, maxSubFactorV); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public JpegColorSpace ColorSpace { get; } |
||||
|
|
||||
|
public bool Interleaved { get; } |
||||
|
|
||||
|
public int PixelHeight { get; } |
||||
|
|
||||
|
public int PixelWidth { get; } |
||||
|
|
||||
|
public Component[] Components { get; } |
||||
|
|
||||
|
public int McusPerLine { get; } |
||||
|
|
||||
|
public int McusPerColumn { get; } |
||||
|
|
||||
|
public int BlocksPerMcu { get; } |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
for (int i = 0; i < this.Components.Length; i++) |
||||
|
{ |
||||
|
this.Components[i].Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void AllocateComponents(bool fullScan) |
||||
|
{ |
||||
|
for (int i = 0; i < this.Components.Length; i++) |
||||
|
{ |
||||
|
Component component = this.Components[i]; |
||||
|
component.AllocateSpectral(fullScan); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,123 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Diagnostics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using SixLabors.ImageSharp.Advanced; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// On-stack worker struct to efficiently encapsulate the TPixel -> L8 -> Y conversion chain of 8x8 pixel blocks.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|
||||
internal ref struct LuminanceForwardConverter<TPixel> |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|
||||
/// </summary>
|
|
||||
private const int PixelsPerSample = 8 * 8; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Y component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F Y; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 64-pixel span to hold unconverted TPixel data.
|
|
||||
/// </summary>
|
|
||||
private readonly Span<TPixel> pixelSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 64-byte span to hold converted <see cref="L8"/> data.
|
|
||||
/// </summary>
|
|
||||
private readonly Span<L8> l8Span; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sampled pixel buffer size.
|
|
||||
/// </summary>
|
|
||||
private readonly Size samplingAreaSize; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// <see cref="Configuration"/> for internal operations.
|
|
||||
/// </summary>
|
|
||||
private readonly Configuration config; |
|
||||
|
|
||||
public LuminanceForwardConverter(ImageFrame<TPixel> frame) |
|
||||
{ |
|
||||
this.Y = default; |
|
||||
|
|
||||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|
||||
this.l8Span = new L8[PixelsPerSample].AsSpan(); |
|
||||
|
|
||||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|
||||
this.config = frame.GetConfiguration(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets size of sampling area from given frame pixel buffer.
|
|
||||
/// </summary>
|
|
||||
private static Size SampleSize => new(8, 8); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>)
|
|
||||
/// </summary>
|
|
||||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|
||||
{ |
|
||||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|
||||
|
|
||||
PixelOperations<TPixel>.Instance.ToL8(this.config, this.pixelSpan, this.l8Span); |
|
||||
|
|
||||
ref Block8x8F yBlock = ref this.Y; |
|
||||
ref L8 l8Start = ref MemoryMarshal.GetReference(this.l8Span); |
|
||||
|
|
||||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|
||||
{ |
|
||||
ConvertAvx(ref l8Start, ref yBlock); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
ConvertScalar(ref l8Start, ref yBlock); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts 8x8 L8 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
|
|
||||
/// </summary>
|
|
||||
/// <param name="l8Start">Start of span of L8 pixels with size of 64</param>
|
|
||||
/// <param name="yBlock">8x8 destination matrix of Luminance(Y) converted data</param>
|
|
||||
private static void ConvertAvx(ref L8 l8Start, ref Block8x8F yBlock) |
|
||||
{ |
|
||||
Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter"); |
|
||||
|
|
||||
ref Vector128<byte> l8ByteSpan = ref Unsafe.As<L8, Vector128<byte>>(ref l8Start); |
|
||||
ref Vector256<float> destRef = ref yBlock.V0; |
|
||||
|
|
||||
const int bytesPerL8Stride = 8; |
|
||||
for (nint i = 0; i < 8; i++) |
|
||||
{ |
|
||||
Unsafe.Add(ref destRef, i) = Avx.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref l8ByteSpan, bytesPerL8Stride * i))); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts 8x8 L8 pixel matrix to 8x8 Block of floats.
|
|
||||
/// </summary>
|
|
||||
/// <param name="l8Start">Start of span of L8 pixels with size of 64</param>
|
|
||||
/// <param name="yBlock">8x8 destination matrix of Luminance(Y) converted data</param>
|
|
||||
private static void ConvertScalar(ref L8 l8Start, ref Block8x8F yBlock) |
|
||||
{ |
|
||||
for (int i = 0; i < Block8x8F.Size; i++) |
|
||||
{ |
|
||||
ref L8 c = ref Unsafe.Add(ref l8Start, i); |
|
||||
yBlock[i] = c.PackedValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,21 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Enumerates the quantization tables.
|
|
||||
/// </summary>
|
|
||||
internal enum QuantIndex |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The luminance quantization table index.
|
|
||||
/// </summary>
|
|
||||
Luminance = 0, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The chrominance quantization table index.
|
|
||||
/// </summary>
|
|
||||
Chrominance = 1, |
|
||||
} |
|
||||
} |
|
||||
@ -1,161 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Diagnostics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using SixLabors.ImageSharp.Advanced; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// On-stack worker struct to convert TPixel -> Rgb24 of 8x8 pixel blocks.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel type to work on.</typeparam>
|
|
||||
internal ref struct RgbForwardConverter<TPixel> |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|
||||
/// </summary>
|
|
||||
private const int PixelsPerSample = 8 * 8; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|
||||
/// </summary>
|
|
||||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Red component.
|
|
||||
/// </summary>
|
|
||||
public Block8x8F R; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Green component.
|
|
||||
/// </summary>
|
|
||||
public Block8x8F G; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Blue component.
|
|
||||
/// </summary>
|
|
||||
public Block8x8F B; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 64-byte span to hold unconverted TPixel data.
|
|
||||
/// </summary>
|
|
||||
private readonly Span<TPixel> pixelSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 64-byte span to hold converted Rgb24 data.
|
|
||||
/// </summary>
|
|
||||
private readonly Span<Rgb24> rgbSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sampled pixel buffer size.
|
|
||||
/// </summary>
|
|
||||
private readonly Size samplingAreaSize; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// <see cref="Configuration"/> for internal operations.
|
|
||||
/// </summary>
|
|
||||
private readonly Configuration config; |
|
||||
|
|
||||
public RgbForwardConverter(ImageFrame<TPixel> frame) |
|
||||
{ |
|
||||
this.R = default; |
|
||||
this.G = default; |
|
||||
this.B = default; |
|
||||
|
|
||||
// temporal pixel buffers
|
|
||||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|
||||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|
||||
|
|
||||
// frame data
|
|
||||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|
||||
this.config = frame.GetConfiguration(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets size of sampling area from given frame pixel buffer.
|
|
||||
/// </summary>
|
|
||||
private static Size SampleSize => new(8, 8); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts a 8x8 image area inside 'pixels' at position (x, y) to Rgb24.
|
|
||||
/// </summary>
|
|
||||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|
||||
{ |
|
||||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|
||||
|
|
||||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|
||||
|
|
||||
ref Block8x8F redBlock = ref this.R; |
|
||||
ref Block8x8F greenBlock = ref this.G; |
|
||||
ref Block8x8F blueBlock = ref this.B; |
|
||||
|
|
||||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|
||||
{ |
|
||||
ConvertAvx(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
ConvertScalar(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts 8x8 RGB24 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rgbSpan">Span of Rgb24 pixels with size of 64</param>
|
|
||||
/// <param name="rBlock">8x8 destination matrix of Red converted data</param>
|
|
||||
/// <param name="gBlock">8x8 destination matrix of Blue converted data</param>
|
|
||||
/// <param name="bBlock">8x8 destination matrix of Green converted data</param>
|
|
||||
private static void ConvertAvx(Span<Rgb24> rgbSpan, ref Block8x8F rBlock, ref Block8x8F gBlock, ref Block8x8F bBlock) |
|
||||
{ |
|
||||
Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter"); |
|
||||
|
|
||||
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|
||||
ref Vector256<float> redRef = ref rBlock.V0; |
|
||||
ref Vector256<float> greenRef = ref gBlock.V0; |
|
||||
ref Vector256<float> blueRef = ref bBlock.V0; |
|
||||
Vector256<byte> zero = Vector256.Create(0).AsByte(); |
|
||||
|
|
||||
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes)); |
|
||||
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb)); |
|
||||
Vector256<byte> rgb, rg, bx; |
|
||||
|
|
||||
const int bytesPerRgbStride = 24; |
|
||||
for (nint i = 0; i < 8; i++) |
|
||||
{ |
|
||||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, bytesPerRgbStride * i).AsUInt32(), extractToLanesMask).AsByte(); |
|
||||
|
|
||||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|
||||
|
|
||||
rg = Avx2.UnpackLow(rgb, zero); |
|
||||
bx = Avx2.UnpackHigh(rgb, zero); |
|
||||
|
|
||||
Unsafe.Add(ref redRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|
||||
Unsafe.Add(ref greenRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|
||||
Unsafe.Add(ref blueRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private static void ConvertScalar(Span<Rgb24> rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock) |
|
||||
{ |
|
||||
ref Rgb24 rgbStart = ref MemoryMarshal.GetReference(rgbSpan); |
|
||||
|
|
||||
for (int i = 0; i < Block8x8F.Size; i++) |
|
||||
{ |
|
||||
Rgb24 c = Unsafe.Add(ref rgbStart, (nint)(uint)i); |
|
||||
|
|
||||
redBlock[i] = c.R; |
|
||||
greenBlock[i] = c.G; |
|
||||
blueBlock[i] = c.B; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,237 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace.
|
|
||||
/// Methods to build the tables are based on libjpeg implementation.
|
|
||||
/// </summary>
|
|
||||
internal unsafe struct RgbToYCbCrConverterLut |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The red luminance table
|
|
||||
/// </summary>
|
|
||||
public fixed int YRTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The green luminance table
|
|
||||
/// </summary>
|
|
||||
public fixed int YGTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The blue luminance table
|
|
||||
/// </summary>
|
|
||||
public fixed int YBTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The red blue-chrominance table
|
|
||||
/// </summary>
|
|
||||
public fixed int CbRTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The green blue-chrominance table
|
|
||||
/// </summary>
|
|
||||
public fixed int CbGTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The blue blue-chrominance table
|
|
||||
/// B=>Cb and R=>Cr are the same
|
|
||||
/// </summary>
|
|
||||
public fixed int CbBTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The green red-chrominance table
|
|
||||
/// </summary>
|
|
||||
public fixed int CrGTable[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The blue red-chrominance table
|
|
||||
/// </summary>
|
|
||||
public fixed int CrBTable[256]; |
|
||||
|
|
||||
// Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places.
|
|
||||
private const int ScaleBits = 16; |
|
||||
|
|
||||
private const int CBCrOffset = 128 << ScaleBits; |
|
||||
|
|
||||
private const int Half = 1 << (ScaleBits - 1); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes the YCbCr tables
|
|
||||
/// </summary>
|
|
||||
/// <returns>The initialized <see cref="RgbToYCbCrConverterLut"/></returns>
|
|
||||
public static RgbToYCbCrConverterLut Create() |
|
||||
{ |
|
||||
RgbToYCbCrConverterLut tables = default; |
|
||||
|
|
||||
for (int i = 0; i <= 255; i++) |
|
||||
{ |
|
||||
// The values for the calculations are left scaled up since we must add them together before rounding.
|
|
||||
tables.YRTable[i] = Fix(0.299F) * i; |
|
||||
tables.YGTable[i] = Fix(0.587F) * i; |
|
||||
tables.YBTable[i] = (Fix(0.114F) * i) + Half; |
|
||||
tables.CbRTable[i] = (-Fix(0.168735892F)) * i; |
|
||||
tables.CbGTable[i] = (-Fix(0.331264108F)) * i; |
|
||||
|
|
||||
// We use a rounding fudge - factor of 0.5 - epsilon for Cb and Cr.
|
|
||||
// This ensures that the maximum output will round to 255
|
|
||||
// not 256, and thus that we don't have to range-limit.
|
|
||||
//
|
|
||||
// B=>Cb and R=>Cr tables are the same
|
|
||||
tables.CbBTable[i] = (Fix(0.5F) * i) + CBCrOffset + Half - 1; |
|
||||
|
|
||||
tables.CrGTable[i] = (-Fix(0.418687589F)) * i; |
|
||||
tables.CrBTable[i] = (-Fix(0.081312411F)) * i; |
|
||||
} |
|
||||
|
|
||||
return tables; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private float CalculateY(byte r, byte g, byte b) |
|
||||
{ |
|
||||
// float y = (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|
||||
return (this.YRTable[r] + this.YGTable[g] + this.YBTable[b]) >> ScaleBits; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private float CalculateCb(byte r, byte g, byte b) |
|
||||
{ |
|
||||
// float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b));
|
|
||||
return (this.CbRTable[r] + this.CbGTable[g] + this.CbBTable[b]) >> ScaleBits; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private float CalculateCr(byte r, byte g, byte b) |
|
||||
{ |
|
||||
// float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b));
|
|
||||
return (this.CbBTable[r] + this.CrGTable[g] + this.CrBTable[b]) >> ScaleBits; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts Rgb24 pixels into YCbCr color space with 4:4:4 subsampling sampling of luminance and chroma.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rgbSpan">Span of Rgb24 pixel data</param>
|
|
||||
/// <param name="yBlock">Resulting Y values block</param>
|
|
||||
/// <param name="cbBlock">Resulting Cb values block</param>
|
|
||||
/// <param name="crBlock">Resulting Cr values block</param>
|
|
||||
public void Convert444(Span<Rgb24> rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) |
|
||||
{ |
|
||||
ref Rgb24 rgbStart = ref rgbSpan[0]; |
|
||||
|
|
||||
for (int i = 0; i < Block8x8F.Size; i++) |
|
||||
{ |
|
||||
Rgb24 c = Unsafe.Add(ref rgbStart, i); |
|
||||
|
|
||||
yBlock[i] = this.CalculateY(c.R, c.G, c.B); |
|
||||
cbBlock[i] = this.CalculateCb(c.R, c.G, c.B); |
|
||||
crBlock[i] = this.CalculateCr(c.R, c.G, c.B); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts Rgb24 pixels into YCbCr color space with 4:2:0 subsampling of luminance and chroma.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>Calculates 2 out of 4 luminance blocks and half of chroma blocks. This method must be called twice per 4x 8x8 DCT blocks with different row param.</remarks>
|
|
||||
/// <param name="rgbSpan">Span of Rgb24 pixel data</param>
|
|
||||
/// <param name="yBlockLeft">First or "left" resulting Y block</param>
|
|
||||
/// <param name="yBlockRight">Second or "right" resulting Y block</param>
|
|
||||
/// <param name="cbBlock">Resulting Cb values block</param>
|
|
||||
/// <param name="crBlock">Resulting Cr values block</param>
|
|
||||
/// <param name="row">Row index of the 16x16 block, 0 or 1</param>
|
|
||||
public void Convert420(Span<Rgb24> rgbSpan, ref Block8x8F yBlockLeft, ref Block8x8F yBlockRight, ref Block8x8F cbBlock, ref Block8x8F crBlock, int row) |
|
||||
{ |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(row, 0, 1, nameof(row)); |
|
||||
|
|
||||
ref float yBlockLeftRef = ref Unsafe.As<Block8x8F, float>(ref yBlockLeft); |
|
||||
ref float yBlockRightRef = ref Unsafe.As<Block8x8F, float>(ref yBlockRight); |
|
||||
|
|
||||
// 0-31 or 32-63
|
|
||||
// upper or lower part
|
|
||||
int chromaWriteOffset = row * (Block8x8F.Size / 2); |
|
||||
ref float cbBlockRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, float>(ref cbBlock), chromaWriteOffset); |
|
||||
ref float crBlockRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, float>(ref crBlock), chromaWriteOffset); |
|
||||
|
|
||||
ref Rgb24 rgbStart = ref rgbSpan[0]; |
|
||||
|
|
||||
for (int i = 0; i < 8; i += 2) |
|
||||
{ |
|
||||
int yBlockWriteOffset = i * 8; |
|
||||
ref Rgb24 stride = ref Unsafe.Add(ref rgbStart, i * 16); |
|
||||
|
|
||||
int chromaOffset = 8 * (i / 2); |
|
||||
|
|
||||
// left
|
|
||||
this.ConvertChunk420( |
|
||||
ref stride, |
|
||||
ref Unsafe.Add(ref yBlockLeftRef, yBlockWriteOffset), |
|
||||
ref Unsafe.Add(ref cbBlockRef, chromaOffset), |
|
||||
ref Unsafe.Add(ref crBlockRef, chromaOffset)); |
|
||||
|
|
||||
// right
|
|
||||
this.ConvertChunk420( |
|
||||
ref Unsafe.Add(ref stride, 8), |
|
||||
ref Unsafe.Add(ref yBlockRightRef, yBlockWriteOffset), |
|
||||
ref Unsafe.Add(ref cbBlockRef, chromaOffset + 4), |
|
||||
ref Unsafe.Add(ref crBlockRef, chromaOffset + 4)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private void ConvertChunk420(ref Rgb24 stride, ref float yBlock, ref float cbBlock, ref float crBlock) |
|
||||
{ |
|
||||
// jpeg 8x8 blocks are processed as 16x16 blocks with 16x8 subpasses (this is done for performance reasons)
|
|
||||
// each row is 16 pixels wide thus +16 stride reference offset
|
|
||||
// resulting luminance (Y`) are sampled at original resolution thus +8 reference offset
|
|
||||
for (int k = 0; k < 8; k += 2) |
|
||||
{ |
|
||||
ref float yBlockRef = ref Unsafe.Add(ref yBlock, k); |
|
||||
|
|
||||
// top row
|
|
||||
Rgb24 px0 = Unsafe.Add(ref stride, k); |
|
||||
Rgb24 px1 = Unsafe.Add(ref stride, k + 1); |
|
||||
yBlockRef = this.CalculateY(px0.R, px0.G, px0.B); |
|
||||
Unsafe.Add(ref yBlockRef, 1) = this.CalculateY(px1.R, px1.G, px1.B); |
|
||||
|
|
||||
// bottom row
|
|
||||
Rgb24 px2 = Unsafe.Add(ref stride, k + 16); |
|
||||
Rgb24 px3 = Unsafe.Add(ref stride, k + 17); |
|
||||
Unsafe.Add(ref yBlockRef, 8) = this.CalculateY(px2.R, px2.G, px2.B); |
|
||||
Unsafe.Add(ref yBlockRef, 9) = this.CalculateY(px3.R, px3.G, px3.B); |
|
||||
|
|
||||
// chroma average for 2x2 pixel block
|
|
||||
Unsafe.Add(ref cbBlock, k / 2) = this.CalculateAverageCb(px0, px1, px2, px3); |
|
||||
Unsafe.Add(ref crBlock, k / 2) = this.CalculateAverageCr(px0, px1, px2, px3); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private float CalculateAverageCb(Rgb24 px0, Rgb24 px1, Rgb24 px2, Rgb24 px3) |
|
||||
{ |
|
||||
return 0.25f |
|
||||
* (this.CalculateCb(px0.R, px0.G, px0.B) |
|
||||
+ this.CalculateCb(px1.R, px1.G, px1.B) |
|
||||
+ this.CalculateCb(px2.R, px2.G, px2.B) |
|
||||
+ this.CalculateCb(px3.R, px3.G, px3.B)); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private float CalculateAverageCr(Rgb24 px0, Rgb24 px1, Rgb24 px2, Rgb24 px3) |
|
||||
{ |
|
||||
return 0.25f |
|
||||
* (this.CalculateCr(px0.R, px0.G, px0.B) |
|
||||
+ this.CalculateCr(px1.R, px1.G, px1.B) |
|
||||
+ this.CalculateCr(px2.R, px2.G, px2.B) |
|
||||
+ this.CalculateCr(px3.R, px3.G, px3.B)); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static int Fix(float x) |
|
||||
=> (int)((x * (1L << ScaleBits)) + 0.5F); |
|
||||
} |
|
||||
} |
|
||||
@ -1,237 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Diagnostics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
internal static class RgbToYCbCrConverterVectorized |
|
||||
{ |
|
||||
public static bool IsSupported => Avx2.IsSupported; |
|
||||
|
|
||||
public static int AvxCompatibilityPadding |
|
||||
{ |
|
||||
// rgb byte matrices contain 8 strides by 8 pixels each, thus 64 pixels total
|
|
||||
// Strides are stored sequentially - one big span of 64 * 3 = 192 bytes
|
|
||||
// Each stride has exactly 3 * 8 = 24 bytes or 3 * 8 * 8 = 192 bits
|
|
||||
// Avx registers are 256 bits so rgb span will be loaded with extra 64 bits from the next stride:
|
|
||||
// stride 0 0 - 192 -(+64bits)-> 256
|
|
||||
// stride 1 192 - 384 -(+64bits)-> 448
|
|
||||
// stride 2 384 - 576 -(+64bits)-> 640
|
|
||||
// stride 3 576 - 768 -(+64bits)-> 832
|
|
||||
// stride 4 768 - 960 -(+64bits)-> 1024
|
|
||||
// stride 5 960 - 1152 -(+64bits)-> 1216
|
|
||||
// stride 6 1152 - 1344 -(+64bits)-> 1408
|
|
||||
// stride 7 1344 - 1536 -(+64bits)-> 1600 <-- READ ACCESS VIOLATION
|
|
||||
//
|
|
||||
// Total size of the 64 pixel rgb span: 64 * 3 * 8 = 1536 bits, avx operations require 1600 bits
|
|
||||
// This is not permitted - we are reading foreign memory
|
|
||||
//
|
|
||||
// 8 byte padding to rgb byte span will solve this problem without extra code in converters
|
|
||||
get |
|
||||
{ |
|
||||
if (IsSupported) |
|
||||
{ |
|
||||
return 8; |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
internal static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[] |
|
||||
{ |
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, |
|
||||
3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 |
|
||||
}; |
|
||||
|
|
||||
internal static ReadOnlySpan<byte> ExtractRgb => new byte[] |
|
||||
{ |
|
||||
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, |
|
||||
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF |
|
||||
}; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts 8x8 Rgb24 pixel matrix to YCbCr pixel matrices with 4:4:4 subsampling
|
|
||||
/// </summary>
|
|
||||
/// <remarks>Total size of rgb span must be 200 bytes</remarks>
|
|
||||
/// <param name="rgbSpan">Span of rgb pixels with size of 64</param>
|
|
||||
/// <param name="yBlock">8x8 destination matrix of Luminance(Y) converted data</param>
|
|
||||
/// <param name="cbBlock">8x8 destination matrix of Chrominance(Cb) converted data</param>
|
|
||||
/// <param name="crBlock">8x8 destination matrix of Chrominance(Cr) converted data</param>
|
|
||||
public static void Convert444(ReadOnlySpan<Rgb24> rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) |
|
||||
{ |
|
||||
Debug.Assert(IsSupported, "AVX2 is required to run this converter"); |
|
||||
|
|
||||
var f0299 = Vector256.Create(0.299f); |
|
||||
var f0587 = Vector256.Create(0.587f); |
|
||||
var f0114 = Vector256.Create(0.114f); |
|
||||
var fn0168736 = Vector256.Create(-0.168736f); |
|
||||
var fn0331264 = Vector256.Create(-0.331264f); |
|
||||
var f128 = Vector256.Create(128f); |
|
||||
var fn0418688 = Vector256.Create(-0.418688f); |
|
||||
var fn0081312F = Vector256.Create(-0.081312F); |
|
||||
var f05 = Vector256.Create(0.5f); |
|
||||
Vector256<byte> zero = Vector256.Create(0).AsByte(); |
|
||||
|
|
||||
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|
||||
ref Vector256<float> destYRef = ref yBlock.V0; |
|
||||
ref Vector256<float> destCbRef = ref cbBlock.V0; |
|
||||
ref Vector256<float> destCrRef = ref crBlock.V0; |
|
||||
|
|
||||
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); |
|
||||
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); |
|
||||
Vector256<byte> rgb, rg, bx; |
|
||||
Vector256<float> r, g, b; |
|
||||
|
|
||||
const int bytesPerRgbStride = 24; |
|
||||
for (int i = 0; i < 8; i++) |
|
||||
{ |
|
||||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte(); |
|
||||
|
|
||||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|
||||
|
|
||||
rg = Avx2.UnpackLow(rgb, zero); |
|
||||
bx = Avx2.UnpackHigh(rgb, zero); |
|
||||
|
|
||||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|
||||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|
||||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|
||||
|
|
||||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|
||||
Unsafe.Add(ref destYRef, i) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|
||||
|
|
||||
// 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b))
|
|
||||
Unsafe.Add(ref destCbRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|
||||
|
|
||||
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
|
|
||||
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts 16x8 Rgb24 pixels matrix to 2 Y 8x8 matrices with 4:2:0 subsampling
|
|
||||
/// </summary>
|
|
||||
public static void Convert420(ReadOnlySpan<Rgb24> rgbSpan, ref Block8x8F yBlockLeft, ref Block8x8F yBlockRight, ref Block8x8F cbBlock, ref Block8x8F crBlock, int row) |
|
||||
{ |
|
||||
Debug.Assert(IsSupported, "AVX2 is required to run this converter"); |
|
||||
|
|
||||
var f0299 = Vector256.Create(0.299f); |
|
||||
var f0587 = Vector256.Create(0.587f); |
|
||||
var f0114 = Vector256.Create(0.114f); |
|
||||
var fn0168736 = Vector256.Create(-0.168736f); |
|
||||
var fn0331264 = Vector256.Create(-0.331264f); |
|
||||
var f128 = Vector256.Create(128f); |
|
||||
var fn0418688 = Vector256.Create(-0.418688f); |
|
||||
var fn0081312F = Vector256.Create(-0.081312F); |
|
||||
var f05 = Vector256.Create(0.5f); |
|
||||
Vector256<byte> zero = Vector256.Create(0).AsByte(); |
|
||||
|
|
||||
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|
||||
|
|
||||
int destOffset = row * 4; |
|
||||
|
|
||||
ref Vector256<float> destCbRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, Vector256<float>>(ref cbBlock), destOffset); |
|
||||
ref Vector256<float> destCrRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, Vector256<float>>(ref crBlock), destOffset); |
|
||||
|
|
||||
Vector256<uint> extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); |
|
||||
Vector256<byte> extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); |
|
||||
Vector256<byte> rgb, rg, bx; |
|
||||
Vector256<float> r, g, b; |
|
||||
|
|
||||
Span<Vector256<float>> rDataLanes = stackalloc Vector256<float>[4]; |
|
||||
Span<Vector256<float>> gDataLanes = stackalloc Vector256<float>[4]; |
|
||||
Span<Vector256<float>> bDataLanes = stackalloc Vector256<float>[4]; |
|
||||
|
|
||||
const int bytesPerRgbStride = 24; |
|
||||
for (int i = 0; i < 4; i++) |
|
||||
{ |
|
||||
// 16x2 => 8x1
|
|
||||
// left 8x8 column conversions
|
|
||||
for (int j = 0; j < 4; j += 2) |
|
||||
{ |
|
||||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * ((i * 4) + j))).AsUInt32(), extractToLanesMask).AsByte(); |
|
||||
|
|
||||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|
||||
|
|
||||
rg = Avx2.UnpackLow(rgb, zero); |
|
||||
bx = Avx2.UnpackHigh(rgb, zero); |
|
||||
|
|
||||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|
||||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|
||||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|
||||
|
|
||||
int yBlockVerticalOffset = (i * 2) + ((j & 2) >> 1); |
|
||||
|
|
||||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|
||||
Unsafe.Add(ref yBlockLeft.V0, yBlockVerticalOffset) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|
||||
|
|
||||
rDataLanes[j] = r; |
|
||||
gDataLanes[j] = g; |
|
||||
bDataLanes[j] = b; |
|
||||
} |
|
||||
|
|
||||
// 16x2 => 8x1
|
|
||||
// right 8x8 column conversions
|
|
||||
for (int j = 1; j < 4; j += 2) |
|
||||
{ |
|
||||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * ((i * 4) + j))).AsUInt32(), extractToLanesMask).AsByte(); |
|
||||
|
|
||||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|
||||
|
|
||||
rg = Avx2.UnpackLow(rgb, zero); |
|
||||
bx = Avx2.UnpackHigh(rgb, zero); |
|
||||
|
|
||||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|
||||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|
||||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|
||||
|
|
||||
int yBlockVerticalOffset = (i * 2) + ((j & 2) >> 1); |
|
||||
|
|
||||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|
||||
Unsafe.Add(ref yBlockRight.V0, yBlockVerticalOffset) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|
||||
|
|
||||
rDataLanes[j] = r; |
|
||||
gDataLanes[j] = g; |
|
||||
bDataLanes[j] = b; |
|
||||
} |
|
||||
|
|
||||
r = Scale16x2_8x1(rDataLanes); |
|
||||
g = Scale16x2_8x1(gDataLanes); |
|
||||
b = Scale16x2_8x1(bDataLanes); |
|
||||
|
|
||||
// 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b))
|
|
||||
Unsafe.Add(ref destCbRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|
||||
|
|
||||
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
|
|
||||
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Scales 16x2 matrix to 8x1 using 2x2 average
|
|
||||
/// </summary>
|
|
||||
/// <param name="v">Input matrix consisting of 4 256bit vectors</param>
|
|
||||
/// <returns>256bit vector containing upper and lower scaled parts of the input matrix</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
internal static Vector256<float> Scale16x2_8x1(ReadOnlySpan<Vector256<float>> v) |
|
||||
{ |
|
||||
Debug.Assert(Avx2.IsSupported, "AVX2 is required to run this converter"); |
|
||||
DebugGuard.IsTrue(v.Length == 4, "Input span must consist of 4 elements"); |
|
||||
|
|
||||
var f025 = Vector256.Create(0.25f); |
|
||||
|
|
||||
Vector256<float> left = Avx.Add(v[0], v[2]); |
|
||||
Vector256<float> right = Avx.Add(v[1], v[3]); |
|
||||
Vector256<float> avg2x2 = Avx.Multiply(Avx.HorizontalAdd(left, right), f025); |
|
||||
|
|
||||
return Avx2.Permute4x64(avg2x2.AsDouble(), 0b11_01_10_00).AsSingle(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,12 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converter used to convert pixel data to jpeg spectral data.
|
||||
|
/// </summary>
|
||||
|
internal abstract class SpectralConverter |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,149 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.Linq; |
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
private readonly ComponentProcessor[] componentProcessors; |
||||
|
|
||||
|
private readonly int pixelRowsPerStep; |
||||
|
|
||||
|
private int pixelRowCounter; |
||||
|
|
||||
|
private readonly Buffer2D<TPixel> pixelBuffer; |
||||
|
|
||||
|
private readonly IMemoryOwner<float> redLane; |
||||
|
|
||||
|
private readonly IMemoryOwner<float> greenLane; |
||||
|
|
||||
|
private readonly IMemoryOwner<float> blueLane; |
||||
|
|
||||
|
private readonly int alignedPixelWidth; |
||||
|
|
||||
|
private readonly JpegColorConverterBase colorConverter; |
||||
|
|
||||
|
public SpectralConverter(JpegFrame frame, Image<TPixel> image, Block8x8F[] dequantTables) |
||||
|
{ |
||||
|
MemoryAllocator allocator = image.GetConfiguration().MemoryAllocator; |
||||
|
|
||||
|
// iteration data
|
||||
|
int majorBlockWidth = frame.Components.Max((component) => component.SizeInBlocks.Width); |
||||
|
int majorVerticalSamplingFactor = frame.Components.Max((component) => component.SamplingFactors.Height); |
||||
|
|
||||
|
const int blockPixelHeight = 8; |
||||
|
this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelHeight; |
||||
|
|
||||
|
// pixel buffer of the image
|
||||
|
this.pixelBuffer = image.GetRootFramePixelBuffer(); |
||||
|
|
||||
|
// component processors from spectral to Rgb24
|
||||
|
const int blockPixelWidth = 8; |
||||
|
this.alignedPixelWidth = majorBlockWidth * blockPixelWidth; |
||||
|
var postProcessorBufferSize = new Size(this.alignedPixelWidth, this.pixelRowsPerStep); |
||||
|
this.componentProcessors = new ComponentProcessor[frame.Components.Length]; |
||||
|
for (int i = 0; i < this.componentProcessors.Length; i++) |
||||
|
{ |
||||
|
Component component = frame.Components[i]; |
||||
|
this.componentProcessors[i] = new ComponentProcessor( |
||||
|
allocator, |
||||
|
component, |
||||
|
postProcessorBufferSize, |
||||
|
dequantTables[component.QuantizationTableIndex]); |
||||
|
} |
||||
|
|
||||
|
this.redLane = allocator.Allocate<float>(this.alignedPixelWidth, AllocationOptions.Clean); |
||||
|
this.greenLane = allocator.Allocate<float>(this.alignedPixelWidth, AllocationOptions.Clean); |
||||
|
this.blueLane = allocator.Allocate<float>(this.alignedPixelWidth, AllocationOptions.Clean); |
||||
|
|
||||
|
// color converter from Rgb24 to YCbCr
|
||||
|
this.colorConverter = JpegColorConverterBase.GetConverter(colorSpace: frame.ColorSpace, precision: 8); |
||||
|
} |
||||
|
|
||||
|
public void ConvertStrideBaseline() |
||||
|
{ |
||||
|
// Codestyle suggests expression body but it
|
||||
|
// also requires empty line before comments
|
||||
|
// which looks ugly with expression bodies thus this warning disable
|
||||
|
#pragma warning disable IDE0022
|
||||
|
// Convert next pixel stride using single spectral `stride'
|
||||
|
// Note that zero passing eliminates the need of virtual call
|
||||
|
// from JpegComponentPostProcessor
|
||||
|
this.ConvertStride(spectralStep: 0); |
||||
|
#pragma warning restore IDE0022
|
||||
|
} |
||||
|
|
||||
|
public void ConvertFull() |
||||
|
{ |
||||
|
int steps = (int)Numerics.DivideCeil((uint)this.pixelBuffer.Height, (uint)this.pixelRowsPerStep); |
||||
|
for (int i = 0; i < steps; i++) |
||||
|
{ |
||||
|
this.ConvertStride(i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ConvertStride(int spectralStep) |
||||
|
{ |
||||
|
int start = this.pixelRowCounter; |
||||
|
int end = start + this.pixelRowsPerStep; |
||||
|
|
||||
|
int pixelBufferLastVerticalIndex = this.pixelBuffer.Height - 1; |
||||
|
|
||||
|
// Pixel strides must be padded with the last pixel of the stride
|
||||
|
int paddingStartIndex = this.pixelBuffer.Width; |
||||
|
int paddedPixelsCount = this.alignedPixelWidth - this.pixelBuffer.Width; |
||||
|
|
||||
|
Span<float> rLane = this.redLane.GetSpan(); |
||||
|
Span<float> gLane = this.greenLane.GetSpan(); |
||||
|
Span<float> bLane = this.blueLane.GetSpan(); |
||||
|
|
||||
|
for (int yy = start; yy < end; yy++) |
||||
|
{ |
||||
|
int y = yy - this.pixelRowCounter; |
||||
|
|
||||
|
// Unpack TPixel to r/g/b planes
|
||||
|
int srcIndex = Math.Min(yy, pixelBufferLastVerticalIndex); |
||||
|
Span<TPixel> sourceRow = this.pixelBuffer.DangerousGetRowSpan(srcIndex); |
||||
|
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(rLane, gLane, bLane, sourceRow); |
||||
|
|
||||
|
rLane.Slice(paddingStartIndex).Fill(rLane[paddingStartIndex - 1]); |
||||
|
gLane.Slice(paddingStartIndex).Fill(gLane[paddingStartIndex - 1]); |
||||
|
bLane.Slice(paddingStartIndex).Fill(bLane[paddingStartIndex - 1]); |
||||
|
|
||||
|
// Convert from rgb24 to target pixel type
|
||||
|
var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y); |
||||
|
this.colorConverter.ConvertFromRgb(values, rLane, gLane, bLane); |
||||
|
} |
||||
|
|
||||
|
// Convert pixels to spectral
|
||||
|
for (int i = 0; i < this.componentProcessors.Length; i++) |
||||
|
{ |
||||
|
this.componentProcessors[i].CopyColorBufferToBlocks(spectralStep); |
||||
|
} |
||||
|
|
||||
|
this.pixelRowCounter = end; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
foreach (ComponentProcessor cpp in this.componentProcessors) |
||||
|
{ |
||||
|
cpp.Dispose(); |
||||
|
} |
||||
|
|
||||
|
this.redLane.Dispose(); |
||||
|
this.greenLane.Dispose(); |
||||
|
this.blueLane.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,121 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.Advanced; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|
||||
internal ref struct YCbCrForwardConverter420<TPixel> |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel}, int)"/> call
|
|
||||
/// </summary>
|
|
||||
private const int PixelsPerSample = 16 * 8; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|
||||
/// </summary>
|
|
||||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The left Y component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F YLeft; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The left Y component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F YRight; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Cb component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F Cb; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Cr component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F Cr; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The color conversion tables
|
|
||||
/// </summary>
|
|
||||
private RgbToYCbCrConverterLut colorTables; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 16x8 block to hold TPixel data
|
|
||||
/// </summary>
|
|
||||
private readonly Span<TPixel> pixelSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal RGB block
|
|
||||
/// </summary>
|
|
||||
private readonly Span<Rgb24> rgbSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sampled pixel buffer size
|
|
||||
/// </summary>
|
|
||||
private readonly Size samplingAreaSize; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// <see cref="Configuration"/> for internal operations
|
|
||||
/// </summary>
|
|
||||
private readonly Configuration config; |
|
||||
|
|
||||
public YCbCrForwardConverter420(ImageFrame<TPixel> frame) |
|
||||
{ |
|
||||
// matrices would be filled during convert calls
|
|
||||
this.YLeft = default; |
|
||||
this.YRight = default; |
|
||||
this.Cb = default; |
|
||||
this.Cr = default; |
|
||||
|
|
||||
// temporal pixel buffers
|
|
||||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|
||||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|
||||
|
|
||||
// frame data
|
|
||||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|
||||
this.config = frame.GetConfiguration(); |
|
||||
|
|
||||
// conversion vector fallback data
|
|
||||
if (!RgbToYCbCrConverterVectorized.IsSupported) |
|
||||
{ |
|
||||
this.colorTables = RgbToYCbCrConverterLut.Create(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.colorTables = default; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets size of sampling area from given frame pixel buffer.
|
|
||||
/// </summary>
|
|
||||
private static Size SampleSize => new(16, 8); |
|
||||
|
|
||||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows, int idx) |
|
||||
{ |
|
||||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|
||||
|
|
||||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|
||||
|
|
||||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|
||||
{ |
|
||||
RgbToYCbCrConverterVectorized.Convert420(this.rgbSpan, ref this.YLeft, ref this.YRight, ref this.Cb, ref this.Cr, idx); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.colorTables.Convert420(this.rgbSpan, ref this.YLeft, ref this.YRight, ref this.Cb, ref this.Cr, idx); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,122 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.Advanced; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|
||||
internal ref struct YCbCrForwardConverter444<TPixel> |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|
||||
/// </summary>
|
|
||||
private const int PixelsPerSample = 8 * 8; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|
||||
/// </summary>
|
|
||||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Y component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F Y; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Cb component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F Cb; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The Cr component
|
|
||||
/// </summary>
|
|
||||
public Block8x8F Cr; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The color conversion tables
|
|
||||
/// </summary>
|
|
||||
private RgbToYCbCrConverterLut colorTables; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 64-byte span to hold unconverted TPixel data
|
|
||||
/// </summary>
|
|
||||
private readonly Span<TPixel> pixelSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Temporal 64-byte span to hold converted Rgb24 data
|
|
||||
/// </summary>
|
|
||||
private readonly Span<Rgb24> rgbSpan; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sampled pixel buffer size
|
|
||||
/// </summary>
|
|
||||
private readonly Size samplingAreaSize; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// <see cref="Configuration"/> for internal operations
|
|
||||
/// </summary>
|
|
||||
private readonly Configuration config; |
|
||||
|
|
||||
public YCbCrForwardConverter444(ImageFrame<TPixel> frame) |
|
||||
{ |
|
||||
// matrices would be filled during convert calls
|
|
||||
this.Y = default; |
|
||||
this.Cb = default; |
|
||||
this.Cr = default; |
|
||||
|
|
||||
// temporal pixel buffers
|
|
||||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|
||||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|
||||
|
|
||||
// frame data
|
|
||||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|
||||
this.config = frame.GetConfiguration(); |
|
||||
|
|
||||
// conversion vector fallback data
|
|
||||
if (!RgbToYCbCrConverterVectorized.IsSupported) |
|
||||
{ |
|
||||
this.colorTables = RgbToYCbCrConverterLut.Create(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.colorTables = default; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets size of sampling area from given frame pixel buffer.
|
|
||||
/// </summary>
|
|
||||
private static Size SampleSize => new(8, 8); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>)
|
|
||||
/// </summary>
|
|
||||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|
||||
{ |
|
||||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|
||||
|
|
||||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|
||||
|
|
||||
ref Block8x8F yBlock = ref this.Y; |
|
||||
ref Block8x8F cbBlock = ref this.Cb; |
|
||||
ref Block8x8F crBlock = ref this.Cr; |
|
||||
|
|
||||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|
||||
{ |
|
||||
RgbToYCbCrConverterVectorized.Convert444(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
this.colorTables.Convert444(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,61 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|
||||
{ |
|
||||
internal static class YCbCrForwardConverter<TPixel> |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
public static void LoadAndStretchEdges(RowOctet<TPixel> source, Span<TPixel> dest, Point start, Size sampleSize, Size totalSize) |
|
||||
{ |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(start.X, 0, totalSize.Width - 1, nameof(start.X)); |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(start.Y, 0, totalSize.Height - 1, nameof(start.Y)); |
|
||||
|
|
||||
int width = Math.Min(sampleSize.Width, totalSize.Width - start.X); |
|
||||
int height = Math.Min(sampleSize.Height, totalSize.Height - start.Y); |
|
||||
|
|
||||
uint byteWidth = (uint)(width * Unsafe.SizeOf<TPixel>()); |
|
||||
int remainderXCount = sampleSize.Width - width; |
|
||||
|
|
||||
ref byte blockStart = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<TPixel, byte>(dest)); |
|
||||
int rowSizeInBytes = sampleSize.Width * Unsafe.SizeOf<TPixel>(); |
|
||||
|
|
||||
for (int y = 0; y < height; y++) |
|
||||
{ |
|
||||
Span<TPixel> row = source[y]; |
|
||||
|
|
||||
ref byte s = ref Unsafe.As<TPixel, byte>(ref row[start.X]); |
|
||||
ref byte d = ref Unsafe.Add(ref blockStart, y * rowSizeInBytes); |
|
||||
|
|
||||
Unsafe.CopyBlock(ref d, ref s, byteWidth); |
|
||||
|
|
||||
ref TPixel last = ref Unsafe.Add(ref Unsafe.As<byte, TPixel>(ref d), width - 1); |
|
||||
|
|
||||
for (int x = 1; x <= remainderXCount; x++) |
|
||||
{ |
|
||||
Unsafe.Add(ref last, x) = last; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
int remainderYCount = sampleSize.Height - height; |
|
||||
|
|
||||
if (remainderYCount == 0) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * rowSizeInBytes); |
|
||||
|
|
||||
for (int y = 1; y <= remainderYCount; y++) |
|
||||
{ |
|
||||
ref byte remStart = ref Unsafe.Add(ref lastRowStart, rowSizeInBytes * y); |
|
||||
Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)rowSizeInBytes); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,15 +1,13 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Six Labors Split License.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Identifies the colorspace of a Jpeg image.
|
/// Identifies the colorspace of a Jpeg image.
|
||||
/// </summary>
|
/// </summary>
|
||||
internal enum JpegColorSpace |
internal enum JpegColorSpace |
||||
{ |
{ |
||||
Undefined = 0, |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Color space with 1 component.
|
/// Color space with 1 component.
|
||||
/// </summary>
|
/// </summary>
|
||||
@ -1,16 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Image decoder for generating an image out of a jpg stream.
|
|
||||
/// </summary>
|
|
||||
internal interface IJpegDecoderOptions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
|
|
||||
/// </summary>
|
|
||||
bool IgnoreMetadata { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,19 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Configuration options for decoding Jpeg images.
|
||||
|
/// </summary>
|
||||
|
public sealed class JpegDecoderOptions : ISpecializedDecoderOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the resize mode.
|
||||
|
/// </summary>
|
||||
|
public JpegDecoderResizeMode ResizeMode { get; set; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public DecoderOptions GeneralOptions { get; set; } = new(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides enumeration for resize modes taken during decoding.
|
||||
|
/// Applicable only when <see cref="DecoderOptions.TargetSize"/> has a value.
|
||||
|
/// </summary>
|
||||
|
public enum JpegDecoderResizeMode |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Both <see cref="IdctOnly"/> and <see cref="ScaleOnly"/>.
|
||||
|
/// </summary>
|
||||
|
Combined, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// IDCT-only to nearest block scale. Similar in output to <see cref="KnownResamplers.Box"/>.
|
||||
|
/// </summary>
|
||||
|
IdctOnly, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Opt-out the IDCT part and only Resize. Can be useful in case of quality concerns.
|
||||
|
/// </summary>
|
||||
|
ScaleOnly |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,196 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Image encoder for writing an image to a stream as a jpeg.
|
||||
|
/// </summary>
|
||||
|
internal sealed unsafe partial class JpegEncoderCore |
||||
|
{ |
||||
|
private static JpegFrameConfig[] CreateFrameConfigs() |
||||
|
{ |
||||
|
var defaultLuminanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 0, HuffmanSpec.LuminanceDC); |
||||
|
var defaultLuminanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 0, HuffmanSpec.LuminanceAC); |
||||
|
var defaultChrominanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 1, HuffmanSpec.ChrominanceDC); |
||||
|
var defaultChrominanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 1, HuffmanSpec.ChrominanceAC); |
||||
|
|
||||
|
var defaultLuminanceQuantTable = new JpegQuantizationTableConfig(0, Quantization.LuminanceTable); |
||||
|
var defaultChrominanceQuantTable = new JpegQuantizationTableConfig(1, Quantization.ChrominanceTable); |
||||
|
|
||||
|
var yCbCrHuffmanConfigs = new JpegHuffmanTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceHuffmanDC, |
||||
|
defaultLuminanceHuffmanAC, |
||||
|
defaultChrominanceHuffmanDC, |
||||
|
defaultChrominanceHuffmanAC, |
||||
|
}; |
||||
|
|
||||
|
var yCbCrQuantTableConfigs = new JpegQuantizationTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceQuantTable, |
||||
|
defaultChrominanceQuantTable, |
||||
|
}; |
||||
|
|
||||
|
return new JpegFrameConfig[] |
||||
|
{ |
||||
|
// YCbCr 4:4:4
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.YCbCr, |
||||
|
JpegEncodingColor.YCbCrRatio444, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
}, |
||||
|
yCbCrHuffmanConfigs, |
||||
|
yCbCrQuantTableConfigs), |
||||
|
|
||||
|
// YCbCr 4:2:2
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.YCbCr, |
||||
|
JpegEncodingColor.YCbCrRatio422, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 2, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
}, |
||||
|
yCbCrHuffmanConfigs, |
||||
|
yCbCrQuantTableConfigs), |
||||
|
|
||||
|
// YCbCr 4:2:0
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.YCbCr, |
||||
|
JpegEncodingColor.YCbCrRatio420, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 2, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
}, |
||||
|
yCbCrHuffmanConfigs, |
||||
|
yCbCrQuantTableConfigs), |
||||
|
|
||||
|
// YCbCr 4:1:1
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.YCbCr, |
||||
|
JpegEncodingColor.YCbCrRatio411, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 4, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
}, |
||||
|
yCbCrHuffmanConfigs, |
||||
|
yCbCrQuantTableConfigs), |
||||
|
|
||||
|
// YCbCr 4:1:0
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.YCbCr, |
||||
|
JpegEncodingColor.YCbCrRatio410, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 4, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
||||
|
}, |
||||
|
yCbCrHuffmanConfigs, |
||||
|
yCbCrQuantTableConfigs), |
||||
|
|
||||
|
// Luminance
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.Grayscale, |
||||
|
JpegEncodingColor.Luminance, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 0, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
}, |
||||
|
new JpegHuffmanTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceHuffmanDC, |
||||
|
defaultLuminanceHuffmanAC |
||||
|
}, |
||||
|
new JpegQuantizationTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceQuantTable |
||||
|
}), |
||||
|
|
||||
|
// Rgb
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.RGB, |
||||
|
JpegEncodingColor.Rgb, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 82, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 71, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 66, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
}, |
||||
|
new JpegHuffmanTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceHuffmanDC, |
||||
|
defaultLuminanceHuffmanAC |
||||
|
}, |
||||
|
new JpegQuantizationTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceQuantTable |
||||
|
}) |
||||
|
{ |
||||
|
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformUnknown |
||||
|
}, |
||||
|
|
||||
|
// Cmyk
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.Cmyk, |
||||
|
JpegEncodingColor.Cmyk, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
}, |
||||
|
new JpegHuffmanTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceHuffmanDC, |
||||
|
defaultLuminanceHuffmanAC |
||||
|
}, |
||||
|
new JpegQuantizationTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceQuantTable |
||||
|
}) |
||||
|
{ |
||||
|
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformUnknown, |
||||
|
}, |
||||
|
|
||||
|
// YccK
|
||||
|
new JpegFrameConfig( |
||||
|
JpegColorSpace.Ycck, |
||||
|
JpegEncodingColor.Ycck, |
||||
|
new JpegComponentConfig[] |
||||
|
{ |
||||
|
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
||||
|
}, |
||||
|
new JpegHuffmanTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceHuffmanDC, |
||||
|
defaultLuminanceHuffmanAC |
||||
|
}, |
||||
|
new JpegQuantizationTableConfig[] |
||||
|
{ |
||||
|
defaultLuminanceQuantTable |
||||
|
}) |
||||
|
{ |
||||
|
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformYcck, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,16 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Png |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The options for decoding png images
|
|
||||
/// </summary>
|
|
||||
internal interface IPngDecoderOptions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
|
|
||||
/// </summary>
|
|
||||
bool IgnoreMetadata { get; } |
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue