-
-
Notifications
You must be signed in to change notification settings - Fork 890
Added Argb IPackedPixel #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [*.cs] | ||
| indent_style = space | ||
| indent_size = 4 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,309 @@ | ||
| using System; | ||
| using System.Numerics; | ||
|
|
||
| namespace ImageSharp | ||
| { | ||
| /// <summary> | ||
| /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. | ||
| /// The color components are stored in alpha, red, green, and blue 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 struct Argb : IPackedPixel<uint>, IEquatable<Argb> | ||
| { | ||
| const int BlueShift = 0; | ||
| const uint BlueMask = 0xFFFFFF00; | ||
| const int GreenShift = 8; | ||
| const uint GreenMask = 0xFFFF00FF; | ||
| const int RedShift = 16; | ||
| const uint RedMask = 0xFF00FFFF; | ||
| const int AlphaShift = 24; | ||
| const uint AlphaMask = 0x00FFFFFF; | ||
|
|
||
| /// <summary> | ||
| /// The maximum byte value. | ||
| /// </summary> | ||
| readonly static Vector4 MaxBytes = new Vector4(255); | ||
|
|
||
| /// <summary> | ||
| /// The half vector value. | ||
| /// </summary> | ||
| readonly static Vector4 Half = new Vector4(0.5F); | ||
|
|
||
| /// <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 Argb(byte r, byte g, byte b, byte a = 255) | ||
| { | ||
| PackedValue = Pack(r, g, b, a); | ||
| } | ||
|
|
||
| /// <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 Argb(float r, float g, float b, float a = 1) | ||
| { | ||
| PackedValue = Pack(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 Argb(Vector3 vector) | ||
| { | ||
| PackedValue = Pack(ref vector); | ||
| } | ||
|
|
||
| /// <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 Argb(Vector4 vector) | ||
| { | ||
| PackedValue = Pack(ref vector); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="Color"/> struct. | ||
| /// </summary> | ||
| /// <param name="packed"> | ||
| /// The packed value. | ||
| /// </param> | ||
| public Argb(uint packed = 0) | ||
| { | ||
| PackedValue = packed; | ||
| } | ||
| /// <summary> | ||
| /// Gets or sets the red component. | ||
| /// </summary> | ||
| public byte R | ||
| { | ||
| get | ||
| { | ||
| return (byte)(PackedValue >> RedShift); | ||
| } | ||
|
|
||
| set | ||
| { | ||
| PackedValue = PackedValue & RedMask | (uint)value << RedShift; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the green component. | ||
| /// </summary> | ||
| public byte G | ||
| { | ||
| get | ||
| { | ||
| return (byte)(PackedValue >> GreenShift); | ||
| } | ||
|
|
||
| set | ||
| { | ||
| PackedValue = PackedValue & GreenMask | (uint)value << GreenShift; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the blue component. | ||
| /// </summary> | ||
| public byte B | ||
| { | ||
| get | ||
| { | ||
| return (byte)(PackedValue >> BlueShift); | ||
| } | ||
|
|
||
| set | ||
| { | ||
| PackedValue = PackedValue & BlueMask | (uint)value << BlueShift; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the alpha component. | ||
| /// </summary> | ||
| public byte A | ||
| { | ||
| get | ||
| { | ||
| return (byte)(PackedValue >> AlphaShift); | ||
| } | ||
|
|
||
| set | ||
| { | ||
| PackedValue = PackedValue & AlphaMask | (uint)value << AlphaShift; | ||
| } | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void PackFromVector4(Vector4 vector) | ||
| { | ||
| PackedValue = Pack(ref vector); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public Vector4 ToVector4() | ||
| { | ||
| return new Vector4(R, G, B, A) / MaxBytes; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public uint PackedValue { get; set; } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void PackFromBytes(byte x, byte y, byte z, byte w) | ||
| { | ||
| PackedValue = Pack(x, y, z, w); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Packs the four floats into a <see cref="uint"/>. | ||
| /// </summary> | ||
| /// <param name="x">The x-component</param> | ||
| /// <param name="y">The y-component</param> | ||
| /// <param name="z">The z-component</param> | ||
| /// <param name="w">The w-component</param> | ||
| /// <returns>The <see cref="uint"/></returns> | ||
| static uint Pack(float x, float y, float z, float w) | ||
| { | ||
| var value = new Vector4(x, y, z, w); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, sorry, didn't see enough of the rest of the code to not use it (I do use
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not using
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @olivif What is the resultant type of I'm sure you know this already but it's an I work with streams a lot and I've lost count of the amount of times that the wrong value has been passed to a I know it's more common to use now but in certain critical codebases like the Roslyn compiler it's still banned. I honestly believe that it's having a negative effect on modern programming. Devs aren't learning their types nor the framework properly as a result and are being careless, using
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JimBobSquarePants this is super interesting, I've actually never run into these type problems due to
I fully agree here. I was fortunate enough to write a lot of C++ before coming to C# but I realize that might not be the norm these days.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're better than I then! I tried C++ once..... Nope, too hard! 😄
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha! Well, it was great at the time and definitely learnt a lot. Would I go back to unmanaged without a really good reason? Probably not 😄 |
||
| return Pack(ref value); | ||
| } | ||
| /// <summary> | ||
| /// Packs the four floats into a <see cref="uint"/>. | ||
| /// </summary> | ||
| /// <param name="x">The x-component</param> | ||
| /// <param name="y">The y-component</param> | ||
| /// <param name="z">The z-component</param> | ||
| /// <param name="w">The w-component</param> | ||
| /// <returns>The <see cref="uint"/></returns> | ||
| static uint Pack(byte x, byte y, byte z, byte w) | ||
| { | ||
| return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Packs a <see cref="Vector3"/> into a uint. | ||
| /// </summary> | ||
| /// <param name="vector">The vector containing the values to pack.</param> | ||
| /// <returns>The <see cref="uint"/> containing the packed values.</returns> | ||
| static uint Pack(ref Vector3 vector) | ||
| { | ||
| var value = new Vector4(vector, 1); | ||
| return Pack(ref value); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Packs a <see cref="Vector4"/> into a uint. | ||
| /// </summary> | ||
| /// <param name="vector">The vector containing the values to pack.</param> | ||
| /// <returns>The <see cref="uint"/> containing the packed values.</returns> | ||
| static uint Pack(ref Vector4 vector) | ||
| { | ||
| vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); | ||
| vector *= MaxBytes; | ||
| vector += Half; | ||
| return (uint)(((byte)vector.X << RedShift) | ||
| | ((byte)vector.Y << GreenShift) | ||
| | ((byte)vector.Z << BlueShift) | ||
| | (byte)vector.W << AlphaShift); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder) | ||
| { | ||
| switch(componentOrder) { | ||
| case ComponentOrder.ZYX: | ||
| bytes[startIndex] = B; | ||
| bytes[startIndex + 1] = G; | ||
| bytes[startIndex + 2] = R; | ||
| break; | ||
| case ComponentOrder.ZYXW: | ||
| bytes[startIndex] = B; | ||
| bytes[startIndex + 1] = G; | ||
| bytes[startIndex + 2] = R; | ||
| bytes[startIndex + 3] = A; | ||
| break; | ||
| case ComponentOrder.XYZ: | ||
| bytes[startIndex] = R; | ||
| bytes[startIndex + 1] = G; | ||
| bytes[startIndex + 2] = B; | ||
| break; | ||
| case ComponentOrder.XYZW: | ||
| bytes[startIndex] = R; | ||
| bytes[startIndex + 1] = G; | ||
| bytes[startIndex + 2] = B; | ||
| bytes[startIndex + 3] = A; | ||
| break; | ||
| default: | ||
| throw new NotSupportedException(); | ||
| } | ||
| } | ||
|
|
||
| /// <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> | ||
| public static bool operator ==(Argb left, Argb right) | ||
| { | ||
| return left.PackedValue == right.PackedValue; | ||
| } | ||
|
|
||
| /// <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> | ||
| public static bool operator !=(Argb left, Argb right) | ||
| { | ||
| return left.PackedValue != right.PackedValue; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override bool Equals(object obj) | ||
| { | ||
| return obj is Argb && Equals((Argb)obj); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public bool Equals(Argb other) | ||
| { | ||
| return PackedValue == other.PackedValue; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public override int GetHashCode() | ||
| { | ||
| // ReSharper disable once NonReadonlyMemberInGetHashCode | ||
| return PackedValue.GetHashCode(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,51 @@ public void Alpha8() | |
| Assert.Equal(bgra, new byte[] { 0, 0, 0, 128 }); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Argb() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great! |
||
| { | ||
| // Test the limits. | ||
| Assert.Equal((uint)0x0, new Argb(Vector4.Zero).PackedValue); | ||
| Assert.Equal(0xFFFFFFFF, new Argb(Vector4.One).PackedValue); | ||
|
|
||
| // Test ToVector4. | ||
| Assert.True(Equal(Vector4.One, new Argb(Vector4.One ).ToVector4())); | ||
| Assert.True(Equal(Vector4.Zero, new Argb(Vector4.Zero ).ToVector4())); | ||
| Assert.True(Equal(Vector4.UnitX, new Argb(Vector4.UnitX).ToVector4())); | ||
| Assert.True(Equal(Vector4.UnitY, new Argb(Vector4.UnitY).ToVector4())); | ||
| Assert.True(Equal(Vector4.UnitZ, new Argb(Vector4.UnitZ).ToVector4())); | ||
| Assert.True(Equal(Vector4.UnitW, new Argb(Vector4.UnitW).ToVector4())); | ||
|
|
||
| // Test clamping. | ||
| Assert.True(Equal(Vector4.Zero, new Argb(Vector4.One * -1234.0f).ToVector4())); | ||
| Assert.True(Equal(Vector4.One, new Argb(Vector4.One * +1234.0f).ToVector4())); | ||
|
|
||
| var x = +0.1f; | ||
| var y = -0.3f; | ||
| var z = +0.5f; | ||
| var w = -0.7f; | ||
| var argb = new Argb(x, y, z, w); | ||
| Assert.Equal(0x001a0080u, argb.PackedValue); | ||
|
|
||
| // Test ordering | ||
| byte[] rgb = new byte[3]; | ||
| byte[] rgba = new byte[4]; | ||
| byte[] bgr = new byte[3]; | ||
| byte[] bgra = new byte[4]; | ||
|
|
||
| argb.ToBytes(rgb, 0, ComponentOrder.XYZ); | ||
| Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 }); | ||
|
|
||
| argb.ToBytes(rgba, 0, ComponentOrder.XYZW); | ||
| Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 }); | ||
|
|
||
| argb.ToBytes(bgr, 0, ComponentOrder.ZYX); | ||
| Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a }); | ||
|
|
||
| argb.ToBytes(bgra, 0, ComponentOrder.ZYXW); | ||
| Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 }); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Bgr565() | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good idea.