mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 676 additions and 323 deletions
@ -0,0 +1,335 @@ |
|||
// <copyright file="Color.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Unpacked pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1.
|
|||
/// The color components are stored in red, green, blue, and alpha order.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
|
|||
/// as it avoids the need to create new values for modification operations.
|
|||
/// </remarks>
|
|||
public partial struct Color : IPixel<Color> |
|||
{ |
|||
/// <summary>
|
|||
/// The shift count for the red component
|
|||
/// </summary>
|
|||
private const int RedShift = 0; |
|||
|
|||
/// <summary>
|
|||
/// The shift count for the green component
|
|||
/// </summary>
|
|||
private const int GreenShift = 8; |
|||
|
|||
/// <summary>
|
|||
/// The shift count for the blue component
|
|||
/// </summary>
|
|||
private const int BlueShift = 16; |
|||
|
|||
/// <summary>
|
|||
/// The shift count for the alpha component
|
|||
/// </summary>
|
|||
private const int AlphaShift = 24; |
|||
|
|||
/// <summary>
|
|||
/// The maximum byte value.
|
|||
/// </summary>
|
|||
private static readonly Vector4 MaxBytes = new Vector4(255); |
|||
|
|||
/// <summary>
|
|||
/// The half vector value.
|
|||
/// </summary>
|
|||
private static readonly Vector4 Half = new Vector4(0.5F); |
|||
|
|||
/// <summary>
|
|||
/// The backing vector for SIMD support.
|
|||
/// </summary>
|
|||
private Vector4 backingVector; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Color"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Color(byte r, byte g, byte b, byte a = 255) |
|||
: this() |
|||
{ |
|||
this.backingVector = new Vector4(r, g, b, a) / MaxBytes; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Color"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <param name="a">The alpha component.</param>
|
|||
public Color(float r, float g, float b, float a = 1) |
|||
: this() |
|||
{ |
|||
this.backingVector = new Vector4(r, g, b, a); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Color"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">
|
|||
/// The vector containing the components for the packed vector.
|
|||
/// </param>
|
|||
public Color(Vector3 vector) |
|||
: this() |
|||
{ |
|||
this.backingVector = new Vector4(vector, 1); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Color"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="vector">
|
|||
/// The vector containing the components for the packed vector.
|
|||
/// </param>
|
|||
public Color(Vector4 vector) |
|||
: this() |
|||
{ |
|||
this.backingVector = vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the red component.
|
|||
/// </summary>
|
|||
public float R |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
return this.backingVector.X; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.backingVector.X = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the green component.
|
|||
/// </summary>
|
|||
public float G |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
return this.backingVector.Y; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.backingVector.Y = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the blue component.
|
|||
/// </summary>
|
|||
public float B |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
return this.backingVector.Z; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.backingVector.Z = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the alpha component.
|
|||
/// </summary>
|
|||
public float A |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
return this.backingVector.W; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.backingVector.W = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Color"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">
|
|||
/// The <see cref="Color"/> on the left side of the operand.
|
|||
/// </param>
|
|||
/// <param name="right">
|
|||
/// The <see cref="Color"/> on the right side of the operand.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static bool operator ==(Color left, Color right) |
|||
{ |
|||
return left.backingVector == right.backingVector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compares two <see cref="Color"/> objects for equality.
|
|||
/// </summary>
|
|||
/// <param name="left">The <see cref="Color"/> on the left side of the operand.</param>
|
|||
/// <param name="right">The <see cref="Color"/> on the right side of the operand.</param>
|
|||
/// <returns>
|
|||
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static bool operator !=(Color left, Color right) |
|||
{ |
|||
return left.backingVector != right.backingVector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="Color"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="hex">
|
|||
/// The hexadecimal representation of the combined color components arranged
|
|||
/// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The <see cref="Color"/>.
|
|||
/// </returns>
|
|||
public static Color FromHex(string hex) |
|||
{ |
|||
return ColorBuilder<Color>.FromHex(hex); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public BulkPixelOperations<Color> CreateBulkOperations() => new Color.BulkOperations(); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void PackFromBytes(byte x, byte y, byte z, byte w) |
|||
{ |
|||
this.backingVector = new Vector4(x, y, z, w) / MaxBytes; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the value of this instance to a hexadecimal string.
|
|||
/// </summary>
|
|||
/// <returns>A hexadecimal string representation of the value.</returns>
|
|||
public string ToHex() |
|||
{ |
|||
Vector4 vector = this.backingVector * MaxBytes; |
|||
vector += Half; |
|||
uint hexOrder = (uint)((byte)vector.X << RedShift | (byte)vector.Y << GreenShift | (byte)vector.Z << BlueShift | (byte)vector.W << AlphaShift); |
|||
return hexOrder.ToString("X8"); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void ToXyzBytes(byte[] bytes, int startIndex) |
|||
{ |
|||
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; |
|||
vector += Half; |
|||
bytes[startIndex] = (byte)vector.X; |
|||
bytes[startIndex + 1] = (byte)vector.Y; |
|||
bytes[startIndex + 2] = (byte)vector.Z; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void ToXyzwBytes(byte[] bytes, int startIndex) |
|||
{ |
|||
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; |
|||
vector += Half; |
|||
bytes[startIndex] = (byte)vector.X; |
|||
bytes[startIndex + 1] = (byte)vector.Y; |
|||
bytes[startIndex + 2] = (byte)vector.Z; |
|||
bytes[startIndex + 2] = (byte)vector.W; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void ToZyxBytes(byte[] bytes, int startIndex) |
|||
{ |
|||
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; |
|||
vector += Half; |
|||
bytes[startIndex] = (byte)vector.Z; |
|||
bytes[startIndex + 1] = (byte)vector.Y; |
|||
bytes[startIndex + 2] = (byte)vector.X; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void ToZyxwBytes(byte[] bytes, int startIndex) |
|||
{ |
|||
Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; |
|||
vector += Half; |
|||
bytes[startIndex] = (byte)vector.Z; |
|||
bytes[startIndex + 1] = (byte)vector.Y; |
|||
bytes[startIndex + 2] = (byte)vector.X; |
|||
bytes[startIndex + 2] = (byte)vector.W; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void PackFromVector4(Vector4 vector) |
|||
{ |
|||
this.backingVector = vector; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Vector4 ToVector4() |
|||
{ |
|||
return this.backingVector; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
return (obj is Color) && this.Equals((Color)obj); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public bool Equals(Color other) |
|||
{ |
|||
return this.backingVector == other.backingVector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a string representation of the packed vector.
|
|||
/// </summary>
|
|||
/// <returns>A string representation of the packed vector.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
return this.ToVector4().ToString(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.backingVector.GetHashCode(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,291 @@ |
|||
// <copyright file="Color32.BulkOperations.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
/// <summary>
|
|||
/// Unpacked pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
|
|||
/// The color components are stored in red, green, blue, and alpha order.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
|
|||
/// as it avoids the need to create new values for modification operations.
|
|||
/// </remarks>
|
|||
public partial struct Color32 |
|||
{ |
|||
/// <summary>
|
|||
/// <see cref="BulkPixelOperations{TColor}"/> implementation optimized for <see cref="Color32"/>.
|
|||
/// </summary>
|
|||
internal class BulkOperations : BulkPixelOperations<Color32> |
|||
{ |
|||
/// <summary>
|
|||
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>
|
|||
/// that works only with `count` divisible by <see cref="Vector{UInt32}.Count"/>.
|
|||
/// </summary>
|
|||
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
|
|||
/// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the dstination vectors.</param>
|
|||
/// <param name="count">The number of pixels to convert.</param>
|
|||
/// <remarks>
|
|||
/// Implementation adapted from:
|
|||
/// <see>
|
|||
/// <cref>http://stackoverflow.com/a/5362789</cref>
|
|||
/// </see>
|
|||
/// TODO: We can replace this implementation in the future using new Vector API-s:
|
|||
/// <see>
|
|||
/// <cref>https://github.com/dotnet/corefx/issues/15957</cref>
|
|||
/// </see>
|
|||
/// </remarks>
|
|||
internal static unsafe void ToVector4SimdAligned(BufferSpan<Color32> sourceColors, BufferSpan<Vector4> destVectors, int count) |
|||
{ |
|||
if (!Vector.IsHardwareAccelerated) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Color32.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!"); |
|||
} |
|||
|
|||
int vecSize = Vector<uint>.Count; |
|||
|
|||
DebugGuard.IsTrue( |
|||
count % vecSize == 0, |
|||
nameof(count), |
|||
"Argument 'count' should divisible by Vector<uint>.Count!"); |
|||
|
|||
Vector<float> bVec = new Vector<float>(256.0f / 255.0f); |
|||
Vector<float> magicFloat = new Vector<float>(32768.0f); |
|||
Vector<uint> magicInt = new Vector<uint>(1191182336); // reinterpreded value of 32768.0f
|
|||
Vector<uint> mask = new Vector<uint>(255); |
|||
|
|||
int unpackedRawCount = count * 4; |
|||
|
|||
ref uint src = ref Unsafe.As<Color32, uint>(ref sourceColors.DangerousGetPinnableReference()); |
|||
|
|||
using (Buffer<uint> tempBuf = new Buffer<uint>( |
|||
unpackedRawCount + Vector<uint>.Count)) |
|||
{ |
|||
uint[] temp = tempBuf.Array; |
|||
float[] fTemp = Unsafe.As<float[]>(temp); |
|||
|
|||
ref UnpackedRGBA tempBase = ref Unsafe.As<uint, UnpackedRGBA>(ref tempBuf[0]); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
uint sVal = Unsafe.Add(ref src, i); |
|||
ref UnpackedRGBA dst = ref Unsafe.Add(ref tempBase, i); |
|||
|
|||
// This call is the bottleneck now:
|
|||
dst.Load(sVal); |
|||
} |
|||
|
|||
for (int i = 0; i < unpackedRawCount; i += vecSize) |
|||
{ |
|||
Vector<uint> vi = new Vector<uint>(temp, i); |
|||
|
|||
vi &= mask; |
|||
vi |= magicInt; |
|||
|
|||
Vector<float> vf = Vector.AsVectorSingle(vi); |
|||
vf = (vf - magicFloat) * bVec; |
|||
vf.CopyTo(fTemp, i); |
|||
} |
|||
|
|||
BufferSpan.Copy(tempBuf.Span.AsBytes(), destVectors.AsBytes(), unpackedRawCount * sizeof(uint)); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void ToVector4(BufferSpan<Color32> sourceColors, BufferSpan<Vector4> destVectors, int count) |
|||
{ |
|||
if (count < 256 || !Vector.IsHardwareAccelerated) |
|||
{ |
|||
// Doesn't worth to bother with SIMD:
|
|||
base.ToVector4(sourceColors, destVectors, count); |
|||
return; |
|||
} |
|||
|
|||
int remainder = count % Vector<uint>.Count; |
|||
|
|||
int alignedCount = count - remainder; |
|||
|
|||
if (alignedCount > 0) |
|||
{ |
|||
ToVector4SimdAligned(sourceColors, destVectors, alignedCount); |
|||
} |
|||
|
|||
if (remainder > 0) |
|||
{ |
|||
sourceColors = sourceColors.Slice(alignedCount); |
|||
destVectors = destVectors.Slice(alignedCount); |
|||
base.ToVector4(sourceColors, destVectors, remainder); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void PackFromXyzBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color32> destColors, int count) |
|||
{ |
|||
ref RGB24 sourceRef = ref Unsafe.As<byte, RGB24>(ref sourceBytes.DangerousGetPinnableReference()); |
|||
ref Color32 destRef = ref destColors.DangerousGetPinnableReference(); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i); |
|||
ref Color32 dp = ref Unsafe.Add(ref destRef, i); |
|||
|
|||
Unsafe.As<Color32, RGB24>(ref dp) = sp; |
|||
dp.A = 255; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void ToXyzBytes(BufferSpan<Color32> sourceColors, BufferSpan<byte> destBytes, int count) |
|||
{ |
|||
ref Color32 sourceRef = ref sourceColors.DangerousGetPinnableReference(); |
|||
ref RGB24 destRef = ref Unsafe.As<byte, RGB24>(ref destBytes.DangerousGetPinnableReference()); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref Color32 sp = ref Unsafe.Add(ref sourceRef, i); |
|||
ref RGB24 dp = ref Unsafe.Add(ref destRef, i); |
|||
|
|||
dp = Unsafe.As<Color32, RGB24>(ref sp); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void PackFromXyzwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color32> destColors, int count) |
|||
{ |
|||
BufferSpan.Copy(sourceBytes, destColors.AsBytes(), count * sizeof(Color32)); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override unsafe void ToXyzwBytes(BufferSpan<Color32> sourceColors, BufferSpan<byte> destBytes, int count) |
|||
{ |
|||
BufferSpan.Copy(sourceColors.AsBytes(), destBytes, count * sizeof(Color32)); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void PackFromZyxBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color32> destColors, int count) |
|||
{ |
|||
ref RGB24 sourceRef = ref Unsafe.As<byte, RGB24>(ref sourceBytes.DangerousGetPinnableReference()); |
|||
ref Color32 destRef = ref destColors.DangerousGetPinnableReference(); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i); |
|||
ref Color32 dp = ref Unsafe.Add(ref destRef, i); |
|||
|
|||
Unsafe.As<Color32, RGB24>(ref dp) = sp.ToZyx(); |
|||
dp.A = 255; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void ToZyxBytes(BufferSpan<Color32> sourceColors, BufferSpan<byte> destBytes, int count) |
|||
{ |
|||
ref Color32 sourceRef = ref sourceColors.DangerousGetPinnableReference(); |
|||
ref RGB24 destRef = ref Unsafe.As<byte, RGB24>(ref destBytes.DangerousGetPinnableReference()); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref Color32 sp = ref Unsafe.Add(ref sourceRef, i); |
|||
ref RGB24 dp = ref Unsafe.Add(ref destRef, i); |
|||
|
|||
dp = Unsafe.As<Color32, RGB24>(ref sp).ToZyx(); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void PackFromZyxwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color32> destColors, int count) |
|||
{ |
|||
ref RGBA32 sourceRef = ref Unsafe.As<byte, RGBA32>(ref sourceBytes.DangerousGetPinnableReference()); |
|||
ref Color32 destRef = ref destColors.DangerousGetPinnableReference(); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i); |
|||
ref Color32 dp = ref Unsafe.Add(ref destRef, i); |
|||
RGBA32 zyxw = sp.ToZyxw(); |
|||
dp = Unsafe.As<RGBA32, Color32>(ref zyxw); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
internal override void ToZyxwBytes(BufferSpan<Color32> sourceColors, BufferSpan<byte> destBytes, int count) |
|||
{ |
|||
ref Color32 sourceRef = ref sourceColors.DangerousGetPinnableReference(); |
|||
ref RGBA32 destRef = ref Unsafe.As<byte, RGBA32>(ref destBytes.DangerousGetPinnableReference()); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref RGBA32 sp = ref Unsafe.As<Color32, RGBA32>(ref Unsafe.Add(ref sourceRef, i)); |
|||
ref RGBA32 dp = ref Unsafe.Add(ref destRef, i); |
|||
dp = sp.ToZyxw(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper struct to manipulate 3-byte RGB data.
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
private struct RGB24 |
|||
{ |
|||
private byte x; |
|||
|
|||
private byte y; |
|||
|
|||
private byte z; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper struct to manipulate 4-byte RGBA data.
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
private struct RGBA32 |
|||
{ |
|||
private byte x; |
|||
|
|||
private byte y; |
|||
|
|||
private byte z; |
|||
|
|||
private byte w; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Value type to store <see cref="Color32"/>-s unpacked into multiple <see cref="uint"/>-s.
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
private struct UnpackedRGBA |
|||
{ |
|||
private uint r; |
|||
|
|||
private uint g; |
|||
|
|||
private uint b; |
|||
|
|||
private uint a; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void Load(uint p) |
|||
{ |
|||
this.r = p; |
|||
this.g = p >> GreenShift; |
|||
this.b = p >> BlueShift; |
|||
this.a = p >> AlphaShift; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue