Skip to content

Commit 91f3b74

Browse files
committed
Added Argb IPackedPixel
1 parent 73e63c0 commit 91f3b74

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[*.cs]
2+
indent_style = space
3+
indent_size = 4
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
using System;
2+
using System.Numerics;
3+
4+
namespace ImageSharp
5+
{
6+
/// <summary>
7+
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
8+
/// The color components are stored in alpha, red, green, and blue order.
9+
/// </summary>
10+
/// <remarks>
11+
/// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
12+
/// as it avoids the need to create new values for modification operations.
13+
/// </remarks>
14+
public class Argb : IPackedPixel<uint>, IEquatable<Argb>
15+
{
16+
const int BlueShift = 0;
17+
const uint BlueMask = 0xFFFFFF00;
18+
const int GreenShift = 8;
19+
const uint GreenMask = 0xFFFF00FF;
20+
const int RedShift = 16;
21+
const uint RedMask = 0xFF00FFFF;
22+
const int AlphaShift = 24;
23+
const uint AlphaMask = 0x00FFFFFF;
24+
25+
/// <summary>
26+
/// The maximum byte value.
27+
/// </summary>
28+
readonly static Vector4 MaxBytes = new Vector4(255);
29+
30+
/// <summary>
31+
/// The half vector value.
32+
/// </summary>
33+
readonly static Vector4 Half = new Vector4(0.5F);
34+
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="Color"/> struct.
37+
/// </summary>
38+
/// <param name="r">The red component.</param>
39+
/// <param name="g">The green component.</param>
40+
/// <param name="b">The blue component.</param>
41+
/// <param name="a">The alpha component.</param>
42+
public Argb(byte r, byte g, byte b, byte a = 255)
43+
{
44+
PackedValue = Pack(r, g, b, a);
45+
}
46+
47+
/// <summary>
48+
/// Initializes a new instance of the <see cref="Color"/> struct.
49+
/// </summary>
50+
/// <param name="r">The red component.</param>
51+
/// <param name="g">The green component.</param>
52+
/// <param name="b">The blue component.</param>
53+
/// <param name="a">The alpha component.</param>
54+
public Argb(float r, float g, float b, float a = 1)
55+
{
56+
PackedValue = Pack(r, g, b, a);
57+
}
58+
59+
/// <summary>
60+
/// Initializes a new instance of the <see cref="Color"/> struct.
61+
/// </summary>
62+
/// <param name="vector">
63+
/// The vector containing the components for the packed vector.
64+
/// </param>
65+
public Argb(Vector3 vector)
66+
{
67+
PackedValue = Pack(ref vector);
68+
}
69+
70+
/// <summary>
71+
/// Initializes a new instance of the <see cref="Color"/> struct.
72+
/// </summary>
73+
/// <param name="vector">
74+
/// The vector containing the components for the packed vector.
75+
/// </param>
76+
public Argb(Vector4 vector)
77+
{
78+
PackedValue = Pack(ref vector);
79+
}
80+
81+
/// <summary>
82+
/// Initializes a new instance of the <see cref="Color"/> struct.
83+
/// </summary>
84+
/// <param name="packed">
85+
/// The packed value.
86+
/// </param>
87+
public Argb(uint packed = 0)
88+
{
89+
PackedValue = packed;
90+
}
91+
/// <summary>
92+
/// Gets or sets the red component.
93+
/// </summary>
94+
public byte R
95+
{
96+
get
97+
{
98+
return (byte)(PackedValue >> RedShift);
99+
}
100+
101+
set
102+
{
103+
PackedValue = PackedValue & RedMask | (uint)value << RedShift;
104+
}
105+
}
106+
107+
/// <summary>
108+
/// Gets or sets the green component.
109+
/// </summary>
110+
public byte G
111+
{
112+
get
113+
{
114+
return (byte)(PackedValue >> GreenShift);
115+
}
116+
117+
set
118+
{
119+
PackedValue = PackedValue & GreenMask | (uint)value << GreenShift;
120+
}
121+
}
122+
123+
/// <summary>
124+
/// Gets or sets the blue component.
125+
/// </summary>
126+
public byte B
127+
{
128+
get
129+
{
130+
return (byte)(PackedValue >> BlueShift);
131+
}
132+
133+
set
134+
{
135+
PackedValue = PackedValue & BlueMask | (uint)value << BlueShift;
136+
}
137+
}
138+
139+
/// <summary>
140+
/// Gets or sets the alpha component.
141+
/// </summary>
142+
public byte A
143+
{
144+
get
145+
{
146+
return (byte)(PackedValue >> AlphaShift);
147+
}
148+
149+
set
150+
{
151+
PackedValue = PackedValue & AlphaMask | (uint)value << AlphaShift;
152+
}
153+
}
154+
155+
/// <inheritdoc/>
156+
public void PackFromVector4(Vector4 vector)
157+
{
158+
PackedValue = Pack(ref vector);
159+
}
160+
161+
/// <inheritdoc/>
162+
public Vector4 ToVector4()
163+
{
164+
return new Vector4(R, G, B, A) / MaxBytes;
165+
}
166+
167+
/// <inheritdoc/>
168+
public uint PackedValue { get; set; }
169+
170+
/// <inheritdoc/>
171+
public void PackFromBytes(byte x, byte y, byte z, byte w)
172+
{
173+
PackedValue = Pack(x, y, z, w);
174+
}
175+
176+
/// <summary>
177+
/// Packs the four floats into a <see cref="uint"/>.
178+
/// </summary>
179+
/// <param name="x">The x-component</param>
180+
/// <param name="y">The y-component</param>
181+
/// <param name="z">The z-component</param>
182+
/// <param name="w">The w-component</param>
183+
/// <returns>The <see cref="uint"/></returns>
184+
static uint Pack(float x, float y, float z, float w)
185+
{
186+
var value = new Vector4(x, y, z, w);
187+
return Pack(ref value);
188+
}
189+
/// <summary>
190+
/// Packs the four floats into a <see cref="uint"/>.
191+
/// </summary>
192+
/// <param name="x">The x-component</param>
193+
/// <param name="y">The y-component</param>
194+
/// <param name="z">The z-component</param>
195+
/// <param name="w">The w-component</param>
196+
/// <returns>The <see cref="uint"/></returns>
197+
static uint Pack(byte x, byte y, byte z, byte w)
198+
{
199+
return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift);
200+
}
201+
202+
/// <summary>
203+
/// Packs a <see cref="Vector3"/> into a uint.
204+
/// </summary>
205+
/// <param name="vector">The vector containing the values to pack.</param>
206+
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
207+
static uint Pack(ref Vector3 vector)
208+
{
209+
var value = new Vector4(vector, 1);
210+
return Pack(ref value);
211+
}
212+
213+
/// <summary>
214+
/// Packs a <see cref="Vector4"/> into a uint.
215+
/// </summary>
216+
/// <param name="vector">The vector containing the values to pack.</param>
217+
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
218+
static uint Pack(ref Vector4 vector)
219+
{
220+
vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One);
221+
vector *= MaxBytes;
222+
vector += Half;
223+
return (uint)(((byte)vector.X << RedShift)
224+
| ((byte)vector.Y << GreenShift)
225+
| ((byte)vector.Z << BlueShift)
226+
| (byte)vector.W << AlphaShift);
227+
}
228+
229+
/// <inheritdoc/>
230+
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
231+
{
232+
switch(componentOrder) {
233+
case ComponentOrder.ZYX:
234+
bytes[startIndex] = B;
235+
bytes[startIndex + 1] = G;
236+
bytes[startIndex + 2] = R;
237+
break;
238+
case ComponentOrder.ZYXW:
239+
bytes[startIndex] = B;
240+
bytes[startIndex + 1] = G;
241+
bytes[startIndex + 2] = R;
242+
bytes[startIndex + 3] = A;
243+
break;
244+
case ComponentOrder.XYZ:
245+
bytes[startIndex] = R;
246+
bytes[startIndex + 1] = G;
247+
bytes[startIndex + 2] = B;
248+
break;
249+
case ComponentOrder.XYZW:
250+
bytes[startIndex] = R;
251+
bytes[startIndex + 1] = G;
252+
bytes[startIndex + 2] = B;
253+
bytes[startIndex + 3] = A;
254+
break;
255+
default:
256+
throw new NotSupportedException();
257+
}
258+
}
259+
260+
/// <inheritdoc/>
261+
public override bool Equals(object obj)
262+
{
263+
var a = obj as Argb;
264+
return (a != null) && Equals(a);
265+
}
266+
267+
/// <inheritdoc/>
268+
public bool Equals(Argb other)
269+
{
270+
return PackedValue == other.PackedValue;
271+
}
272+
273+
/// <inheritdoc/>
274+
public override int GetHashCode()
275+
{
276+
// ReSharper disable once NonReadonlyMemberInGetHashCode
277+
return PackedValue.GetHashCode();
278+
}
279+
}
280+
}

tests/ImageSharp.Tests/Colors/PackedPixelTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,51 @@ public void Alpha8()
5656
Assert.Equal(bgra, new byte[] { 0, 0, 0, 128 });
5757
}
5858

59+
[Fact]
60+
public void Argb()
61+
{
62+
// Test the limits.
63+
Assert.Equal((uint)0x0, new Argb(Vector4.Zero).PackedValue);
64+
Assert.Equal(0xFFFFFFFF, new Argb(Vector4.One).PackedValue);
65+
66+
// Test ToVector4.
67+
Assert.True(Equal(Vector4.One, new Argb(Vector4.One ).ToVector4()));
68+
Assert.True(Equal(Vector4.Zero, new Argb(Vector4.Zero ).ToVector4()));
69+
Assert.True(Equal(Vector4.UnitX, new Argb(Vector4.UnitX).ToVector4()));
70+
Assert.True(Equal(Vector4.UnitY, new Argb(Vector4.UnitY).ToVector4()));
71+
Assert.True(Equal(Vector4.UnitZ, new Argb(Vector4.UnitZ).ToVector4()));
72+
Assert.True(Equal(Vector4.UnitW, new Argb(Vector4.UnitW).ToVector4()));
73+
74+
// Test clamping.
75+
Assert.True(Equal(Vector4.Zero, new Argb(Vector4.One * -1234.0f).ToVector4()));
76+
Assert.True(Equal(Vector4.One, new Argb(Vector4.One * +1234.0f).ToVector4()));
77+
78+
var x = +0.1f;
79+
var y = -0.3f;
80+
var z = +0.5f;
81+
var w = -0.7f;
82+
var argb = new Argb(x, y, z, w);
83+
Assert.Equal(0x001a0080u, argb.PackedValue);
84+
85+
// Test ordering
86+
byte[] rgb = new byte[3];
87+
byte[] rgba = new byte[4];
88+
byte[] bgr = new byte[3];
89+
byte[] bgra = new byte[4];
90+
91+
argb.ToBytes(rgb, 0, ComponentOrder.XYZ);
92+
Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 });
93+
94+
argb.ToBytes(rgba, 0, ComponentOrder.XYZW);
95+
Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 });
96+
97+
argb.ToBytes(bgr, 0, ComponentOrder.ZYX);
98+
Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a });
99+
100+
argb.ToBytes(bgra, 0, ComponentOrder.ZYXW);
101+
Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 });
102+
}
103+
59104
[Fact]
60105
public void Bgr565()
61106
{

0 commit comments

Comments
 (0)