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