Browse Source

Encode EXR image with float pixel data

pull/3096/head
Brian Popow 4 years ago
parent
commit
799dafe414
  1. 5
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  2. 104
      src/ImageSharp/Formats/ImageExtensions.Save.cs
  3. 1
      src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs
  4. 5
      src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs
  5. 38
      src/ImageSharp/Formats/OpenExr/ExrEncoder.cs
  6. 310
      src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs
  7. 9
      src/ImageSharp/Formats/OpenExr/ExrMetadata.cs
  8. 5
      src/ImageSharp/Formats/OpenExr/ExrPixelType.cs
  9. 16
      src/ImageSharp/Formats/OpenExr/IExrEncoderOptions.cs
  10. 21
      src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs
  11. 59
      src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs

5
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -57,10 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
public int Offset { get; }
public static BmpFileHeader Parse(Span<byte> data)
{
return MemoryMarshal.Cast<byte, BmpFileHeader>(data)[0];
}
public static BmpFileHeader Parse(Span<byte> data) => MemoryMarshal.Cast<byte, BmpFileHeader>(data)[0];
public void WriteTo(Span<byte> buffer)
{

104
src/ImageSharp/Formats/ImageExtensions.Save.cs

@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.OpenExr;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
@ -847,5 +848,108 @@ namespace SixLabors.ImageSharp
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);
// foo
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsExr(this Image source, string path) => SaveAsExr(source, path, null);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, string path) => SaveAsExrAsync(source, path, null);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsExrAsync(source, path, null, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsExr(this Image source, string path, ExrEncoder encoder) =>
source.Save(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ExrFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, string path, ExrEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ExrFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsExr(this Image source, Stream stream)
=> SaveAsExr(source, stream, null);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsExrAsync(source, stream, null, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static void SaveAsExr(this Image source, Stream stream, ExrEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ExrFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, Stream stream, ExrEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ExrFormat.Instance),
cancellationToken);
}
}

1
src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(ExrFormat.Instance, new ExrEncoder());
configuration.ImageFormatsManager.SetDecoder(ExrFormat.Instance, new ExrDecoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new ExrImageFormatDetector());
}

5
src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs

@ -90,6 +90,9 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
case ExrPixelType.Float:
this.DecodeFloatingPointPixelData(stream, pixels);
break;
case ExrPixelType.UnsignedInt:
this.DecodeUnsignedIntPixelData(stream, pixels);
break;
default:
ExrThrowHelper.ThrowNotSupported("Pixel type is not supported");
break;
@ -349,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
}
// Next three bytes contain info's about the image.
// We ignore those for now.
// TODO: We ignore those for now.
stream.Read(this.buffer, 0, 3);
ExrHeader header = this.ParseHeader(stream);

38
src/ImageSharp/Formats/OpenExr/ExrEncoder.cs

@ -0,0 +1,38 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.OpenExr
{
/// <summary>
/// Image encoder for writing an image to a stream in the OpenExr Format.
/// </summary>
public sealed class ExrEncoder : IImageEncoder, IExrEncoderOptions
{
/// <summary>
/// Gets or sets the pixel type of the image.
/// </summary>
public ExrPixelType? PixelType { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new ExrEncoderCore(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new ExrEncoderCore(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}
}

310
src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs

@ -0,0 +1,310 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.OpenExr
{
/// <summary>
/// Image encoder for writing an image to a stream in the OpenExr format.
/// </summary>
internal sealed class ExrEncoderCore : IImageEncoderInternals
{
/// <summary>
/// Reusable buffer.
/// </summary>
private readonly byte[] buffer = new byte[8];
/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// The pixel type of the image.
/// </summary>
private ExrPixelType? pixelType;
/// <summary>
/// Initializes a new instance of the <see cref="ExrEncoderCore"/> class.
/// </summary>
/// <param name="options">The encoder options.</param>
/// <param name="memoryAllocator">The memory manager.</param>
public ExrEncoderCore(IExrEncoderOptions options, MemoryAllocator memoryAllocator)
{
this.memoryAllocator = memoryAllocator;
this.pixelType = options.PixelType;
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="cancellationToken">The token to request cancellation.</param>
public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
ExrMetadata exrMetadata = metadata.GetExrMetadata();
this.pixelType ??= exrMetadata.PixelType;
int width = image.Width;
int height = image.Height;
ExrPixelType pixelType = ExrPixelType.Float;
var header = new ExrHeader()
{
Compression = ExrCompression.None,
AspectRatio = 1.0f,
DataWindow = new ExrBox2i(0, 0, width - 1, height - 1),
DisplayWindow = new ExrBox2i(0, 0, width - 1, height - 1),
LineOrder = ExrLineOrder.IncreasingY,
ScreenWindowCenter = new PointF(0.0f, 0.0f),
ScreenWindowWidth = 1,
Channels = new List<ExrChannelInfo>()
{
new("B", pixelType, 0, 1, 1),
new("G", pixelType, 0, 1, 1),
new("R", pixelType, 0, 1, 1),
}
};
// Write magick bytes.
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, ExrConstants.MagickBytes);
stream.Write(this.buffer.AsSpan(0, 4));
// Version number.
this.buffer[0] = 2;
// Second, third and fourth bytes store info about the image, all set to zero.
this.buffer[1] = 0;
this.buffer[2] = 0;
this.buffer[3] = 0;
stream.Write(this.buffer.AsSpan(0, 4));
// Write EXR header.
this.WriteHeader(stream, header);
// Write pixel data.
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer;
using IMemoryOwner<float> rgbBuffer = this.memoryAllocator.Allocate<float>(width * 3);
Span<float> redBuffer = rgbBuffer.GetSpan().Slice(0, width);
Span<float> blueBuffer = rgbBuffer.GetSpan().Slice(width, width);
Span<float> greenBuffer = rgbBuffer.GetSpan().Slice(width * 2, width);
// Write offsets to each pixel row.
uint rowSizeBytes = (uint)(width * 3 * 4);
this.WriteRowOffsets(stream, height, rowSizeBytes);
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
for (int x = 0; x < width; x++)
{
var vector4 = pixelRowSpan[x].ToVector4();
redBuffer[x] = vector4.X;
greenBuffer[x] = vector4.Y;
blueBuffer[x] = vector4.Z;
}
// Write row index.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y);
stream.Write(this.buffer.AsSpan(0, 4));
// Write pixel row data size.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, rowSizeBytes);
stream.Write(this.buffer.AsSpan(0, 4));
for (int x = 0; x < width; x++)
{
this.WriteSingle(stream, blueBuffer[x]);
}
for (int x = 0; x < width; x++)
{
this.WriteSingle(stream, greenBuffer[x]);
}
for (int x = 0; x < width; x++)
{
this.WriteSingle(stream, redBuffer[x]);
}
}
}
private void WriteHeader(Stream stream, ExrHeader header)
{
this.WriteChannels(stream, header.Channels);
this.WriteCompression(stream, header.Compression.Value);
this.WriteDataWindow(stream, header.DataWindow.Value);
this.WriteDisplayWindow(stream, header.DisplayWindow.Value);
this.WritePixelAspectRatio(stream, header.AspectRatio.Value);
this.WriteLineOrder(stream, header.LineOrder.Value);
this.WriteScreenWindowCenter(stream, header.ScreenWindowCenter.Value);
this.WriteScreenWindowWidth(stream, header.ScreenWindowWidth.Value);
stream.WriteByte(0);
}
private void WriteRowOffsets(Stream stream, int height, uint rowSizeBytes)
{
ulong startOfPixelData = (ulong)stream.Position + (8 * (ulong)height);
ulong offset = startOfPixelData;
for (int i = 0; i < height; i++)
{
BinaryPrimitives.WriteUInt64LittleEndian(this.buffer, offset);
stream.Write(this.buffer);
offset += 4 + 4 + rowSizeBytes;
}
}
private void WriteChannels(Stream stream, IList<ExrChannelInfo> channels)
{
int attributeSize = 0;
foreach (ExrChannelInfo channelInfo in channels)
{
attributeSize += channelInfo.ChannelName.Length + 1;
attributeSize += 16;
}
// Last zero byte.
attributeSize++;
this.WriteAttributeInformation(stream, "channels", "chlist", attributeSize);
foreach (ExrChannelInfo channelInfo in channels)
{
this.WriteChannelInfo(stream, channelInfo);
}
// Last byte should be zero.
stream.WriteByte(0);
}
private void WriteCompression(Stream stream, ExrCompression compression)
{
this.WriteAttributeInformation(stream, "compression", "compression", 1);
stream.WriteByte((byte)compression);
}
private void WritePixelAspectRatio(Stream stream, float aspectRatio)
{
this.WriteAttributeInformation(stream, "pixelAspectRatio", "float", 4);
this.WriteSingle(stream, aspectRatio);
}
private void WriteLineOrder(Stream stream, ExrLineOrder lineOrder)
{
this.WriteAttributeInformation(stream, "lineOrder", "lineOrder", 1);
stream.WriteByte((byte)lineOrder);
}
private void WriteScreenWindowCenter(Stream stream, PointF screenWindowCenter)
{
this.WriteAttributeInformation(stream, "screenWindowCenter", "v2f", 8);
this.WriteSingle(stream, screenWindowCenter.X);
this.WriteSingle(stream, screenWindowCenter.Y);
}
private void WriteScreenWindowWidth(Stream stream, float screenWindowWidth)
{
this.WriteAttributeInformation(stream, "screenWindowWidth", "float", 4);
this.WriteSingle(stream, screenWindowWidth);
}
private void WriteDataWindow(Stream stream, ExrBox2i dataWindow)
{
this.WriteAttributeInformation(stream, "dataWindow", "box2i", 16);
this.WriteBox2i(stream, dataWindow);
}
private void WriteDisplayWindow(Stream stream, ExrBox2i displayWindow)
{
this.WriteAttributeInformation(stream, "displayWindow", "box2i", 16);
this.WriteBox2i(stream, displayWindow);
}
private void WriteAttributeInformation(Stream stream, string name, string type, int size)
{
// Write attribute name.
this.WriteString(stream, name);
// Write attribute type.
this.WriteString(stream, type);
// Write attribute size.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)size);
stream.Write(this.buffer.AsSpan(0, 4));
}
private void WriteChannelInfo(Stream stream, ExrChannelInfo channelInfo)
{
this.WriteString(stream, channelInfo.ChannelName);
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, (int)channelInfo.PixelType);
stream.Write(this.buffer.AsSpan(0, 4));
stream.WriteByte(channelInfo.PLinear);
// Next 3 bytes are reserved and will set to zero.
stream.WriteByte(0);
stream.WriteByte(0);
stream.WriteByte(0);
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.XSampling);
stream.Write(this.buffer.AsSpan(0, 4));
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.YSampling);
stream.Write(this.buffer.AsSpan(0, 4));
}
private void WriteString(Stream stream, string str)
{
foreach (char c in str)
{
stream.WriteByte((byte)c);
}
// Write termination byte.
stream.WriteByte(0);
}
private void WriteBox2i(Stream stream, ExrBox2i box)
{
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMin);
stream.Write(this.buffer.AsSpan(0, 4));
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.YMin);
stream.Write(this.buffer.AsSpan(0, 4));
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMax);
stream.Write(this.buffer.AsSpan(0, 4));
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.YMax);
stream.Write(this.buffer.AsSpan(0, 4));
}
private unsafe void WriteSingle(Stream stream, float value)
{
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, *(int*)&value);
stream.Write(this.buffer.AsSpan(0, 4));
}
}
}

9
src/ImageSharp/Formats/OpenExr/ExrMetadata.cs

@ -19,9 +19,12 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
/// Initializes a new instance of the <see cref="ExrMetadata"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private ExrMetadata(ExrMetadata other)
{
}
private ExrMetadata(ExrMetadata other) => this.PixelType = other.PixelType;
/// <summary>
/// Gets or sets the pixel format.
/// </summary>
public ExrPixelType PixelType { get; set; } = ExrPixelType.Half;
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new ExrMetadata(this);

5
src/ImageSharp/Formats/OpenExr/ExrPixelType.cs

@ -3,7 +3,10 @@
namespace SixLabors.ImageSharp.Formats.OpenExr
{
internal enum ExrPixelType
/// <summary>
/// The different pixel formats for a OpenEXR image.
/// </summary>
public enum ExrPixelType
{
/// <summary>
/// unsigned int (32 bit).

16
src/ImageSharp/Formats/OpenExr/IExrEncoderOptions.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.OpenExr
{
/// <summary>
/// Configuration options for use during OpenExr encoding.
/// </summary>
internal interface IExrEncoderOptions
{
/// <summary>
/// Gets the pixel type of the image.
/// </summary>
ExrPixelType? PixelType { get; }
}
}

21
src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.OpenExr;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the open exr format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="ExrMetadata"/>.</returns>
public static ExrMetadata GetExrMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(ExrFormat.Instance);
}
}

59
src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs

@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats
{
@ -13,8 +14,29 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public partial struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
{
/// <summary>
/// Gets or sets the red component.
/// </summary>
public float R;
/// <summary>
/// Gets or sets the green component.
/// </summary>
public float G;
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public float B;
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public float A;
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4"/> struct.
/// </summary>
@ -23,18 +45,43 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public HalfVector4(float x, float y, float z, float w)
: this(new Vector4(x, y, z, w))
{
this.R = x;
this.G = y;
this.B = z;
this.A = w;
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components</param>
public HalfVector4(Vector4 vector) => this.PackedValue = Pack(ref vector);
public HalfVector4(Vector4 vector)
{
this.R = vector.X;
this.G = vector.Y;
this.B = vector.Z;
this.A = vector.W;
}
/// <summary>
/// Gets or sets the packed representation of the HalfVector4 struct.
/// </summary>
public ulong HalfVector
{
[MethodImpl(InliningOptions.ShortMethod)]
readonly get => Unsafe.As<HalfVector4, ulong>(ref Unsafe.AsRef(this));
[MethodImpl(InliningOptions.ShortMethod)]
set => Unsafe.As<HalfVector4, ulong>(ref this) = value;
}
/// <inheritdoc/>
public ulong PackedValue { get; set; }
public ulong PackedValue
{
readonly get => this.HalfVector;
set => this.HalfVector = value;
}
/// <summary>
/// Compares two <see cref="HalfVector4"/> objects for equality.
@ -86,11 +133,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]
public readonly Vector4 ToVector4() => new(
HalfTypeHelper.Unpack((ushort)this.PackedValue),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30)));
public readonly Vector4 ToVector4() => new(this.R, this.G, this.B, this.A);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]

Loading…
Cancel
Save