diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d138b..52611e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ This library uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.8.0 + +### Added + +- Added utility methods to pack `Rgba` data into an `int` and vice versa: + - `ToRgbaInt` + - `ToArgbInt` + - `FromRgbaInt` + - `FromArgbInt` + +### Changed + +- Renamed `Color` to `Rgba` to avoid conflicts with System.Drawing. +- Renamed `ReadColor` in `BinaryReaderExtensions` to `ReadRgba`. +- Renamed `RandomColor` in `RandomExtensions` to `RandomRgba`. +- The `A` parameter in the `Rgba` constructor is now optional. + +### Removed + +- Removed `ReadableColorForBrightness` from `Rgba`. + ## 0.7.0 ### Added diff --git a/src/Detach.Tests/Tests/Numerics/RgbaTests.cs b/src/Detach.Tests/Tests/Numerics/RgbaTests.cs new file mode 100644 index 0000000..d3273d0 --- /dev/null +++ b/src/Detach.Tests/Tests/Numerics/RgbaTests.cs @@ -0,0 +1,30 @@ +using Detach.Numerics; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Numerics; + +namespace Detach.Tests.Tests.Numerics; + +[TestClass] +public class RgbaTests +{ + [DataTestMethod] + [DataRow(0, 0, 0, 0)] + [DataRow(1, 2, 3, 4)] + [DataRow(5, 6, 7, 8)] + [DataRow(255, 6, 255, 8)] + [DataRow(255, 255, 255, 255)] + public void RgbaConversions(int r, int g, int b, int a) + { + Rgba expectedRgba = new((byte)r, (byte)g, (byte)b, (byte)a); + Rgba expectedRgb = expectedRgba with { A = byte.MaxValue }; + + Assert.AreEqual(expectedRgba, Rgba.FromVector4(new Vector4(r / 255f, g / 255f, b / 255f, a / 255f))); + Assert.AreEqual(expectedRgb, Rgba.FromVector3(new Vector3(r / 255f, g / 255f, b / 255f))); + + Assert.AreEqual(expectedRgba, Rgba.FromRgbaInt(expectedRgba.ToRgbaInt())); + Assert.AreEqual(expectedRgb, Rgba.FromRgbaInt(expectedRgb.ToRgbaInt())); + + Assert.AreEqual(expectedRgba, Rgba.FromArgbInt(expectedRgba.ToArgbInt())); + Assert.AreEqual(expectedRgb, Rgba.FromArgbInt(expectedRgb.ToArgbInt())); + } +} diff --git a/src/Detach/Detach.csproj b/src/Detach/Detach.csproj index 7fecbcd..afe9130 100644 --- a/src/Detach/Detach.csproj +++ b/src/Detach/Detach.csproj @@ -14,7 +14,7 @@ Copyright © Noah Stolk git https://github.com/NoahStolk/Detach - 0.7.0 + 0.8.0 diff --git a/src/Detach/Extensions/BinaryReaderExtensions.cs b/src/Detach/Extensions/BinaryReaderExtensions.cs index 8fe959f..125661d 100644 --- a/src/Detach/Extensions/BinaryReaderExtensions.cs +++ b/src/Detach/Extensions/BinaryReaderExtensions.cs @@ -55,9 +55,9 @@ public static Matrix4x4 ReadMatrix4x4(this BinaryReader br) br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); } - public static Color ReadColor(this BinaryReader br) + public static Rgba ReadRgba(this BinaryReader br) { - return new Color(br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte()); + return new Rgba(br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte()); } public static List ReadLengthPrefixedList(this BinaryReader br, Func reader) diff --git a/src/Detach/Extensions/BinaryWriterExtensions.cs b/src/Detach/Extensions/BinaryWriterExtensions.cs index fe0ab89..f1ccc51 100644 --- a/src/Detach/Extensions/BinaryWriterExtensions.cs +++ b/src/Detach/Extensions/BinaryWriterExtensions.cs @@ -73,12 +73,12 @@ public static void Write(this BinaryWriter bw, Matrix4x4 matrix) bw.Write(matrix.M44); } - public static void Write(this BinaryWriter bw, Color color) + public static void Write(this BinaryWriter bw, Rgba rgba) { - bw.Write(color.R); - bw.Write(color.G); - bw.Write(color.B); - bw.Write(color.A); + bw.Write(rgba.R); + bw.Write(rgba.G); + bw.Write(rgba.B); + bw.Write(rgba.A); } public static void WriteLengthPrefixedList(this BinaryWriter bw, List list, Action writer) diff --git a/src/Detach/Extensions/RandomExtensions.cs b/src/Detach/Extensions/RandomExtensions.cs index 3b84d95..a55a1f6 100644 --- a/src/Detach/Extensions/RandomExtensions.cs +++ b/src/Detach/Extensions/RandomExtensions.cs @@ -486,33 +486,33 @@ public static Vector4 RandomVector4(this Random random, Vector4 minValue, Vector } /// - /// Returns a random with values that are greater than or equal to the corresponding min parameters, and less than the corresponding max parameters. + /// Returns a random with values that are greater than or equal to the corresponding min parameters, and less than the corresponding max parameters. /// /// The instance. - /// The minimum X value for the . - /// The maximum X value for the . - /// The minimum Y value for the . - /// The maximum Y value for the . - /// The minimum Z value for the . - /// The maximum Z value for the . - /// The minimum W value for the . - /// The maximum W value for the . - /// The random . - public static Color RandomColor(this Random random, byte minValueX, byte maxValueX, byte minValueY, byte maxValueY, byte minValueZ, byte maxValueZ, byte minValueW, byte maxValueW) + /// The minimum X value for the . + /// The maximum X value for the . + /// The minimum Y value for the . + /// The maximum Y value for the . + /// The minimum Z value for the . + /// The maximum Z value for the . + /// The minimum W value for the . + /// The maximum W value for the . + /// The random . + public static Rgba RandomRgba(this Random random, byte minValueX, byte maxValueX, byte minValueY, byte maxValueY, byte minValueZ, byte maxValueZ, byte minValueW, byte maxValueW) { - return new Color(random.RandomByte(minValueX, maxValueX), random.RandomByte(minValueY, maxValueY), random.RandomByte(minValueZ, maxValueZ), random.RandomByte(minValueW, maxValueW)); + return new Rgba(random.RandomByte(minValueX, maxValueX), random.RandomByte(minValueY, maxValueY), random.RandomByte(minValueZ, maxValueZ), random.RandomByte(minValueW, maxValueW)); } /// - /// Returns a random with values that are all greater than or equal to , and less than . + /// Returns a random with values that are all greater than or equal to , and less than . /// /// The instance. /// The minimum value. /// The maximum value. - /// The random . - public static Color RandomColor(this Random random, Color minValue, Color maxValue) + /// The random . + public static Rgba RandomRgba(this Random random, Rgba minValue, Rgba maxValue) { - return random.RandomColor(minValue.R, maxValue.R, minValue.G, maxValue.G, minValue.B, maxValue.B, minValue.A, maxValue.A); + return random.RandomRgba(minValue.R, maxValue.R, minValue.G, maxValue.G, minValue.B, maxValue.B, minValue.A, maxValue.A); } /// diff --git a/src/Detach/Inline.cs b/src/Detach/Inline.cs index b6b0e8e..9ea843d 100644 --- a/src/Detach/Inline.cs +++ b/src/Detach/Inline.cs @@ -70,7 +70,7 @@ public static ReadOnlySpan Span(Quaternion value, ReadOnlySpan forma return _buffer.AsSpan(0, charsWritten); } - public static ReadOnlySpan Span(Color value, ReadOnlySpan format = default, IFormatProvider? provider = default) + public static ReadOnlySpan Span(Rgba value, ReadOnlySpan format = default, IFormatProvider? provider = default) { int charsWritten = 0; TryWrite(_buffer, ref charsWritten, value.R, format, provider); diff --git a/src/Detach/Numerics/Color.cs b/src/Detach/Numerics/Color.cs deleted file mode 100644 index 338dec0..0000000 --- a/src/Detach/Numerics/Color.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Detach.Utils; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Detach.Numerics; - -public readonly record struct Color(byte R, byte G, byte B, byte A) -{ - public static Color Invisible { get; } = new(byte.MinValue, byte.MinValue, byte.MinValue, byte.MinValue); - - public static Color White { get; } = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - public static Color Black { get; } = new(byte.MinValue, byte.MinValue, byte.MinValue, byte.MaxValue); - - public static Color HalfTransparentWhite { get; } = new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue / 2); - public static Color HalfTransparentBlack { get; } = new(byte.MinValue, byte.MinValue, byte.MinValue, byte.MaxValue / 2); - - public static Color Red { get; } = new(byte.MaxValue, byte.MinValue, byte.MinValue, byte.MaxValue); - public static Color Green { get; } = new(byte.MinValue, byte.MaxValue, byte.MinValue, byte.MaxValue); - public static Color Blue { get; } = new(byte.MinValue, byte.MinValue, byte.MaxValue, byte.MaxValue); - - public static Color Yellow { get; } = new(byte.MaxValue, byte.MaxValue, byte.MinValue, byte.MaxValue); - public static Color Aqua { get; } = new(byte.MinValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); - public static Color Purple { get; } = new(byte.MaxValue, byte.MinValue, byte.MaxValue, byte.MaxValue); - - public static Color Orange { get; } = new(byte.MaxValue, byte.MaxValue / 2, byte.MinValue, byte.MaxValue); - - public static implicit operator Vector3(Color color) - { - return new Vector3(color.R / (float)byte.MaxValue, color.G / (float)byte.MaxValue, color.B / (float)byte.MaxValue); - } - - public static implicit operator Vector4(Color color) - { - return new Vector4(color.R / (float)byte.MaxValue, color.G / (float)byte.MaxValue, color.B / (float)byte.MaxValue, color.A / (float)byte.MaxValue); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Color operator +(Color left, Color right) - { - return new Color((byte)(left.R + right.R), (byte)(left.G + right.G), (byte)(left.B + right.B), (byte)(left.A + right.A)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Color operator -(Color left, Color right) - { - return new Color((byte)(left.R - right.R), (byte)(left.G - right.G), (byte)(left.B - right.B), (byte)(left.A - right.A)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Color operator *(Color left, Color right) - { - return new Color((byte)(left.R * right.R), (byte)(left.G * right.G), (byte)(left.B * right.B), (byte)(left.A * right.A)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Color operator /(Color left, Color right) - { - return new Color((byte)(left.R / right.R), (byte)(left.G / right.G), (byte)(left.B / right.B), (byte)(left.A / right.A)); - } - - public Color ReadableColorForBrightness() - { - return GetPerceivedBrightness() < 140 ? White : Black; - } - - public int GetPerceivedBrightness() - { - return (int)Math.Sqrt(R * R * 0.299 + G * G * 0.587 + B * B * 0.114); - } - - public Color Intensify(byte component) - { - byte r = (byte)Math.Min(byte.MaxValue, R + component); - byte g = (byte)Math.Min(byte.MaxValue, G + component); - byte b = (byte)Math.Min(byte.MaxValue, B + component); - byte a = (byte)Math.Min(byte.MaxValue, A + component); - return new Color(r, g, b, a); - } - - public Color Desaturate(float f) - { - float r = R / (float)byte.MaxValue; - float g = G / (float)byte.MaxValue; - float b = B / (float)byte.MaxValue; - - float l = 0.3f * r + 0.6f * g + 0.1f * b; - float newR = r + f * (l - r); - float newG = g + f * (l - g); - float newB = b + f * (l - b); - - return new Color((byte)(newR * byte.MaxValue), (byte)(newG * byte.MaxValue), (byte)(newB * byte.MaxValue), A); - } - - public Color Darken(float amount) - { - return new Color((byte)(R * (1 - amount)), (byte)(G * (1 - amount)), (byte)(B * (1 - amount)), A); - } - - public int GetHue() - { - byte min = Math.Min(Math.Min(R, G), B); - byte max = Math.Max(Math.Max(R, G), B); - - if (min == max) - return 0; - - float hue; - if (max == R) - hue = (G - B) / (float)(max - min); - else if (max == G) - hue = 2f + (B - R) / (float)(max - min); - else - hue = 4f + (R - G) / (float)(max - min); - - hue *= 60; - if (hue < 0) - hue += 360; - - return (int)Math.Round(hue); - } - - public static Color Lerp(Color value1, Color value2, float amount) - { - float r = MathUtils.Lerp(value1.R, value2.R, amount); - float g = MathUtils.Lerp(value1.G, value2.G, amount); - float b = MathUtils.Lerp(value1.B, value2.B, amount); - float a = MathUtils.Lerp(value1.A, value2.A, amount); - return new Color((byte)r, (byte)g, (byte)b, (byte)a); - } - - public static Color Invert(Color color) - { - return new Color((byte)(byte.MaxValue - color.R), (byte)(byte.MaxValue - color.G), (byte)(byte.MaxValue - color.B), color.A); - } - - public static Color Gray(float value) - { - if (value is < 0 or > 1) - throw new ArgumentOutOfRangeException(nameof(value)); - - byte component = (byte)(value * byte.MaxValue); - return new Color(component, component, component, byte.MaxValue); - } - - public static Color FromHsv(float hue, float saturation, float value) - { - saturation = Math.Clamp(saturation, 0, 1); - value = Math.Clamp(value, 0, 1); - - int hi = (int)MathF.Floor(hue / 60) % 6; - float f = hue / 60 - MathF.Floor(hue / 60); - - value *= byte.MaxValue; - byte v = (byte)value; - byte p = (byte)(value * (1 - saturation)); - byte q = (byte)(value * (1 - f * saturation)); - byte t = (byte)(value * (1 - (1 - f) * saturation)); - - return hi switch - { - 0 => new Color(v, t, p, byte.MaxValue), - 1 => new Color(q, v, p, byte.MaxValue), - 2 => new Color(p, v, t, byte.MaxValue), - 3 => new Color(p, q, v, byte.MaxValue), - 4 => new Color(t, p, v, byte.MaxValue), - _ => new Color(v, p, q, byte.MaxValue), - }; - } - - public static Color FromVector3(Vector3 vector) - { - return new Color((byte)(vector.X * byte.MaxValue), (byte)(vector.Y * byte.MaxValue), (byte)(vector.Z * byte.MaxValue), byte.MaxValue); - } - - public static Color FromVector4(Vector4 vector) - { - return new Color((byte)(vector.X * byte.MaxValue), (byte)(vector.Y * byte.MaxValue), (byte)(vector.Z * byte.MaxValue), (byte)(vector.W * byte.MaxValue)); - } -} diff --git a/src/Detach/Numerics/Rgba.cs b/src/Detach/Numerics/Rgba.cs new file mode 100644 index 0000000..4294827 --- /dev/null +++ b/src/Detach/Numerics/Rgba.cs @@ -0,0 +1,194 @@ +using Detach.Utils; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Detach.Numerics; + +public readonly record struct Rgba(byte R, byte G, byte B, byte A = byte.MaxValue) +{ + public static Rgba Invisible => default; + + public static Rgba White => new(byte.MaxValue, byte.MaxValue, byte.MaxValue); + public static Rgba Black => new(byte.MinValue, byte.MinValue, byte.MinValue); + + public static Rgba HalfTransparentWhite => new(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue / 2); + public static Rgba HalfTransparentBlack => new(byte.MinValue, byte.MinValue, byte.MinValue, byte.MaxValue / 2); + + public static Rgba Red => new(byte.MaxValue, byte.MinValue, byte.MinValue); + public static Rgba Green => new(byte.MinValue, byte.MaxValue, byte.MinValue); + public static Rgba Blue => new(byte.MinValue, byte.MinValue, byte.MaxValue); + + public static Rgba Yellow => new(byte.MaxValue, byte.MaxValue, byte.MinValue); + public static Rgba Aqua => new(byte.MinValue, byte.MaxValue, byte.MaxValue); + public static Rgba Purple => new(byte.MaxValue, byte.MinValue, byte.MaxValue); + + public static Rgba Orange => new(byte.MaxValue, byte.MaxValue / 2, byte.MinValue); + + public static implicit operator Vector3(Rgba rgba) + { + return new Vector3(rgba.R / (float)byte.MaxValue, rgba.G / (float)byte.MaxValue, rgba.B / (float)byte.MaxValue); + } + + public static implicit operator Vector4(Rgba rgba) + { + return new Vector4(rgba.R / (float)byte.MaxValue, rgba.G / (float)byte.MaxValue, rgba.B / (float)byte.MaxValue, rgba.A / (float)byte.MaxValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba operator +(Rgba left, Rgba right) + { + return new Rgba((byte)(left.R + right.R), (byte)(left.G + right.G), (byte)(left.B + right.B), (byte)(left.A + right.A)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba operator -(Rgba left, Rgba right) + { + return new Rgba((byte)(left.R - right.R), (byte)(left.G - right.G), (byte)(left.B - right.B), (byte)(left.A - right.A)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba operator *(Rgba left, Rgba right) + { + return new Rgba((byte)(left.R * right.R), (byte)(left.G * right.G), (byte)(left.B * right.B), (byte)(left.A * right.A)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgba operator /(Rgba left, Rgba right) + { + return new Rgba((byte)(left.R / right.R), (byte)(left.G / right.G), (byte)(left.B / right.B), (byte)(left.A / right.A)); + } + + public int GetPerceivedBrightness() + { + return (int)Math.Sqrt(R * R * 0.299 + G * G * 0.587 + B * B * 0.114); + } + + public Rgba Intensify(byte component) + { + byte r = (byte)Math.Min(byte.MaxValue, R + component); + byte g = (byte)Math.Min(byte.MaxValue, G + component); + byte b = (byte)Math.Min(byte.MaxValue, B + component); + byte a = (byte)Math.Min(byte.MaxValue, A + component); + return new Rgba(r, g, b, a); + } + + public Rgba Desaturate(float f) + { + float r = R / (float)byte.MaxValue; + float g = G / (float)byte.MaxValue; + float b = B / (float)byte.MaxValue; + + float l = 0.3f * r + 0.6f * g + 0.1f * b; + float newR = r + f * (l - r); + float newG = g + f * (l - g); + float newB = b + f * (l - b); + + return new Rgba((byte)(newR * byte.MaxValue), (byte)(newG * byte.MaxValue), (byte)(newB * byte.MaxValue), A); + } + + public Rgba Darken(float amount) + { + return new Rgba((byte)(R * (1 - amount)), (byte)(G * (1 - amount)), (byte)(B * (1 - amount)), A); + } + + public int GetHue() + { + byte min = Math.Min(Math.Min(R, G), B); + byte max = Math.Max(Math.Max(R, G), B); + + if (min == max) + return 0; + + float hue; + if (max == R) + hue = (G - B) / (float)(max - min); + else if (max == G) + hue = 2f + (B - R) / (float)(max - min); + else + hue = 4f + (R - G) / (float)(max - min); + + hue *= 60; + if (hue < 0) + hue += 360; + + return (int)Math.Round(hue); + } + + public static Rgba Lerp(Rgba value1, Rgba value2, float amount) + { + float r = MathUtils.Lerp(value1.R, value2.R, amount); + float g = MathUtils.Lerp(value1.G, value2.G, amount); + float b = MathUtils.Lerp(value1.B, value2.B, amount); + float a = MathUtils.Lerp(value1.A, value2.A, amount); + return new Rgba((byte)r, (byte)g, (byte)b, (byte)a); + } + + public static Rgba Invert(Rgba rgba) + { + return new Rgba((byte)(byte.MaxValue - rgba.R), (byte)(byte.MaxValue - rgba.G), (byte)(byte.MaxValue - rgba.B), rgba.A); + } + + public static Rgba Gray(float value) + { + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(value)); + + byte component = (byte)(value * byte.MaxValue); + return new Rgba(component, component, component); + } + + public static Rgba FromHsv(float hue, float saturation, float value) + { + saturation = Math.Clamp(saturation, 0, 1); + value = Math.Clamp(value, 0, 1); + + int hi = (int)MathF.Floor(hue / 60) % 6; + float f = hue / 60 - MathF.Floor(hue / 60); + + value *= byte.MaxValue; + byte v = (byte)value; + byte p = (byte)(value * (1 - saturation)); + byte q = (byte)(value * (1 - f * saturation)); + byte t = (byte)(value * (1 - (1 - f) * saturation)); + + return hi switch + { + 0 => new Rgba(v, t, p), + 1 => new Rgba(q, v, p), + 2 => new Rgba(p, v, t), + 3 => new Rgba(p, q, v), + 4 => new Rgba(t, p, v), + _ => new Rgba(v, p, q), + }; + } + + public static Rgba FromVector3(Vector3 vector) + { + return new Rgba((byte)(vector.X * byte.MaxValue), (byte)(vector.Y * byte.MaxValue), (byte)(vector.Z * byte.MaxValue)); + } + + public static Rgba FromVector4(Vector4 vector) + { + return new Rgba((byte)(vector.X * byte.MaxValue), (byte)(vector.Y * byte.MaxValue), (byte)(vector.Z * byte.MaxValue), (byte)(vector.W * byte.MaxValue)); + } + + public int ToRgbaInt() + { + return (R << 24) + (G << 16) + (B << 8) + A; + } + + public int ToArgbInt() + { + return (A << 24) + (R << 16) + (G << 8) + B; + } + + public static Rgba FromRgbaInt(int rgba) + { + return new Rgba((byte)(rgba >> 24), (byte)(rgba >> 16), (byte)(rgba >> 8), (byte)rgba); + } + + public static Rgba FromArgbInt(int argb) + { + return new Rgba((byte)(argb >> 16), (byte)(argb >> 8), (byte)argb, (byte)(argb >> 24)); + } +} diff --git a/src/Detach/Parsers/Texture/TgaFormat/TgaImageData.cs b/src/Detach/Parsers/Texture/TgaFormat/TgaImageData.cs index 46dd398..149166c 100644 --- a/src/Detach/Parsers/Texture/TgaFormat/TgaImageData.cs +++ b/src/Detach/Parsers/Texture/TgaFormat/TgaImageData.cs @@ -57,16 +57,16 @@ public byte[] Read() if (isRlePacket) { - Color color = ReadColor(_pixelDepth, _data.Slice(readPosition, bytesPerPixel).AsSpan()); + Rgba rgba = ReadRgba(_pixelDepth, _data.Slice(readPosition, bytesPerPixel).AsSpan()); readPosition += bytesPerPixel; for (int k = 0; k < packetLength; k++) { int pixelWriteIndex = (i * _width + j) * 4; - bytes[pixelWriteIndex + 0] = color.R; - bytes[pixelWriteIndex + 1] = color.G; - bytes[pixelWriteIndex + 2] = color.B; - bytes[pixelWriteIndex + 3] = color.A; + bytes[pixelWriteIndex + 0] = rgba.R; + bytes[pixelWriteIndex + 1] = rgba.G; + bytes[pixelWriteIndex + 2] = rgba.B; + bytes[pixelWriteIndex + 3] = rgba.A; j += columnIncrement; } @@ -75,14 +75,14 @@ public byte[] Read() { for (int k = 0; k < packetLength; k++) { - Color color = ReadColor(_pixelDepth, _data.Slice(readPosition, bytesPerPixel).AsSpan()); + Rgba rgba = ReadRgba(_pixelDepth, _data.Slice(readPosition, bytesPerPixel).AsSpan()); readPosition += bytesPerPixel; int pixelWriteIndex = (i * _width + j) * 4; - bytes[pixelWriteIndex + 0] = color.R; - bytes[pixelWriteIndex + 1] = color.G; - bytes[pixelWriteIndex + 2] = color.B; - bytes[pixelWriteIndex + 3] = color.A; + bytes[pixelWriteIndex + 0] = rgba.R; + bytes[pixelWriteIndex + 1] = rgba.G; + bytes[pixelWriteIndex + 2] = rgba.B; + bytes[pixelWriteIndex + 3] = rgba.A; j += columnIncrement; } @@ -99,12 +99,12 @@ public byte[] Read() for (int j = columnStart; _rightToLeft ? j >= 0 : j < _width; j += columnIncrement) { int pixelReadIndex = (i * _width + j) * bytesPerPixel; - Color color = ReadColor(_pixelDepth, _data.Slice(pixelReadIndex, bytesPerPixel).AsSpan()); + Rgba rgba = ReadRgba(_pixelDepth, _data.Slice(pixelReadIndex, bytesPerPixel).AsSpan()); - bytes[writePosition + 0] = color.R; - bytes[writePosition + 1] = color.G; - bytes[writePosition + 2] = color.B; - bytes[writePosition + 3] = color.A; + bytes[writePosition + 0] = rgba.R; + bytes[writePosition + 1] = rgba.G; + bytes[writePosition + 2] = rgba.B; + bytes[writePosition + 3] = rgba.A; writePosition += 4; } } @@ -129,13 +129,13 @@ public void Write(BinaryWriter binaryWriter) { for (int j = 0; j < _width; j++) { - Color currentColor = ReadColor(_pixelDepth, _data.Slice((i * _width + j) * bytesPerPixel, bytesPerPixel).AsSpan()); + Rgba currentRgba = ReadRgba(_pixelDepth, _data.Slice((i * _width + j) * bytesPerPixel, bytesPerPixel).AsSpan()); int amountOfIdenticalPixels = 1; while (j + amountOfIdenticalPixels < _width && amountOfIdenticalPixels < 128) { - Color nextColor = ReadColor(_pixelDepth, _data.Slice((i * _width + j + amountOfIdenticalPixels) * bytesPerPixel, bytesPerPixel).AsSpan()); - if (currentColor == nextColor) + Rgba nextRgba = ReadRgba(_pixelDepth, _data.Slice((i * _width + j + amountOfIdenticalPixels) * bytesPerPixel, bytesPerPixel).AsSpan()); + if (currentRgba == nextRgba) amountOfIdenticalPixels++; else break; @@ -150,10 +150,10 @@ public void Write(BinaryWriter binaryWriter) if (isRlePacket) { binaryWriter.Write((byte)(0b1000_0000 | (amountOfIdenticalPixels - 1))); - binaryWriter.Write(currentColor.R); - binaryWriter.Write(currentColor.G); - binaryWriter.Write(currentColor.B); - binaryWriter.Write(currentColor.A); + binaryWriter.Write(currentRgba.R); + binaryWriter.Write(currentRgba.G); + binaryWriter.Write(currentRgba.B); + binaryWriter.Write(currentRgba.A); } else { @@ -162,22 +162,22 @@ public void Write(BinaryWriter binaryWriter) for (int k = 0; k < amountOfIdenticalPixels; k++) { - binaryWriter.Write(currentColor.R); - binaryWriter.Write(currentColor.G); - binaryWriter.Write(currentColor.B); - binaryWriter.Write(currentColor.A); + binaryWriter.Write(currentRgba.R); + binaryWriter.Write(currentRgba.G); + binaryWriter.Write(currentRgba.B); + binaryWriter.Write(currentRgba.A); } } } } } - private static Color ReadColor(TgaPixelDepth pixelDepth, ReadOnlySpan span) + private static Rgba ReadRgba(TgaPixelDepth pixelDepth, ReadOnlySpan span) { byte b = span[0]; byte g = span[1]; byte r = span[2]; byte a = pixelDepth == TgaPixelDepth.Bgra ? span[3] : (byte)0xFF; - return new Color(r, g, b, a); + return new Rgba(r, g, b, a); } }