From 6b1e5c36ba10e7b958d5fe1ec5102ef65f3ceca4 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 10 Dec 2024 15:06:41 -0800 Subject: [PATCH] Add 2bpp indexed buffer +minor cleanup on 4bpp indexed buffer --- .../Driver/Buffers/BufferIndexed2.cs | 218 ++++++++++++++++++ .../Driver/Buffers/BufferIndexed4.cs | 29 ++- 2 files changed, 232 insertions(+), 15 deletions(-) create mode 100644 Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed2.cs diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed2.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed2.cs new file mode 100644 index 0000000000..b2514d1298 --- /dev/null +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed2.cs @@ -0,0 +1,218 @@ +using Meadow.Peripherals.Displays; +using System; +using System.Linq; + +namespace Meadow.Foundation.Graphics.Buffers +{ + /// + /// Represents a 2bpp pixel buffer with indexed colors + /// Each pixel is represented by 2 bits, allowing for 4 distinct colors + /// + public class BufferIndexed2 : PixelBufferBase + { + /// + /// Color mode of the buffer + /// + public override ColorMode ColorMode => ColorMode.Format2bppIndexed; + + /// + /// The indexed colors as a 4 element array of Color values + /// + public readonly Color[] IndexedColors = new Color[4]; + + /// + /// Create a new BufferIndexed2 object + /// + /// The width in pixels + /// The height in pixels + /// The backing buffer + public BufferIndexed2(int width, int height, byte[] buffer) : base(width, height, buffer) { } + + /// + /// Create a new BufferIndexed2 object + /// + /// The width in pixels + /// The height in pixels + public BufferIndexed2(int width, int height) : base(width, height) { } + + /// + /// Create a new BufferIndexed2 object + /// + public BufferIndexed2() : base() { } + + /// + /// Fill buffer with a color + /// + /// The fill color + public override void Fill(Color color) + { + byte colorValue = (byte)GetIndexForColor(color); + Buffer[0] = (byte)(colorValue << 2 | colorValue << 4 | colorValue << 6); + + int arrayMidPoint = Buffer.Length / 2; + int copyLength; + + for (copyLength = 1; copyLength < arrayMidPoint; copyLength <<= 1) + { + Array.Copy(Buffer, 0, Buffer, copyLength, copyLength); + } + + Array.Copy(Buffer, 0, Buffer, copyLength, Buffer.Length - copyLength); + } + + /// + /// Fill a rectangular area with a color + /// + /// X start position in pixels + /// Y start position in pixels + /// Width in pixels + /// Height in pixels + /// The fill color + public override void Fill(int x, int y, int width, int height, Color color) + { + if (x < 0 || x + width > Width || + y < 0 || y + height > Height) + { + throw new ArgumentOutOfRangeException(); + } + + //TODO optimize + var index = GetIndexForColor(color); + + for (int i = 0; i < width; i++) + { + for (int j = 0; j < height; j++) + { + SetPixel(x + i, y + j, index); + } + } + } + + /// + /// Get the pixel color at a given coordinate + /// + /// The X pixel position + /// The Y pixel position + /// The pixel color + public override Color GetPixel(int x, int y) + { + var index = GetColorIndexForPixel(x, y); + return IndexedColors[index]; + } + + /// + /// Set the pixel color + /// + /// X pixel position + /// Y pixel position + /// The pixel color + public override void SetPixel(int x, int y, Color color) + { + var index = GetIndexForColor(color); + SetPixel(x, y, index); + } + + /// + /// Set the pixel using a color index + /// + /// X pixel position + /// Y pixel position + /// The color index (0-3) + public void SetPixel(int x, int y, int colorIndex) + { + int byteIndex = (y * Width + x) >> 2; // divide by 4 to find the byte + int pixelOffset = (x & 0x03) << 1; // (x % 4)*2 bits offset + + // Clear current 2 bits + Buffer[byteIndex] &= (byte)~(0x03 << pixelOffset); + // Set new bits + Buffer[byteIndex] |= (byte)((colorIndex & 0x03) << pixelOffset); + } + + /// + /// Invert the pixel color + /// + /// x position of pixel + /// y position of pixel + public override void InvertPixel(int x, int y) + { + throw new NotImplementedException("InvertPixel not supported for indexed buffers"); + } + + /// + /// Write a buffer into this buffer at a specified location + /// + /// x origin + /// y origin + /// buffer to write + public override void WriteBuffer(int x, int y, IPixelBuffer buffer) + { + if (buffer.ColorMode == ColorMode && + x % 4 == 0 && + buffer.Width % 4 == 0) + { + // We can do a direct block copy row by row + int sourceIndex, destinationIndex; + int length = buffer.Width / 2; + + for (int i = 0; i < buffer.Height; i++) + { + sourceIndex = length * i; + destinationIndex = (Width * (y + i) + x) >> 2; + + Array.Copy(buffer.Buffer, sourceIndex, Buffer, destinationIndex, length); + } + } + else + { // fall back to a slow write + base.WriteBuffer(x, y, buffer); + } + } + + /// + /// Get the pixel's color index (0-3) at a given coordinate + /// + /// The X pixel position + /// The Y pixel position + /// The 2-bit color index + public byte GetColorIndexForPixel(int x, int y) + { + int byteIndex = (y * Width + x) >> 2; + int pixelOffset = (x & 0x03) << 1; + + byte value = (byte)((Buffer[byteIndex] >> pixelOffset) & 0x03); + return value; + } + + Color GetClosestColor(Color color) + { + return IndexedColors[GetIndexForColor(color)]; + } + + int GetIndexForColor(Color color) + { + if (IndexedColors == null || IndexedColors.All(x => x == null)) + { + throw new NullReferenceException("No indexed colors assigned"); + } + + int closestIndex = -1; + double shortestDistance = double.MaxValue; + + for (int i = 0; i < IndexedColors.Length; i++) + { + if (IndexedColors[i] != null) + { + double distance = GetColorDistance(color, IndexedColors[i]); + if (distance < shortestDistance) + { + shortestDistance = distance; + closestIndex = i; + if (distance == 0) { break; } // perfect match + } + } + } + return closestIndex; + } + } +} diff --git a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed4.cs b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed4.cs index ccfd5e26d6..2f7a3be538 100644 --- a/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed4.cs +++ b/Source/Meadow.Foundation.Libraries_and_Frameworks/Graphics.MicroGraphics/Driver/Buffers/BufferIndexed4.cs @@ -6,6 +6,7 @@ namespace Meadow.Foundation.Graphics.Buffers { /// /// Represents a 4bpp pixel buffer with indexed colors + /// Each pixel is represented by 4 bits, allowing for 8 distinct colors /// public class BufferIndexed4 : PixelBufferBase { @@ -60,7 +61,7 @@ public override void Fill(Color color) } /// - /// Fill with a color + /// Fill a rectangular area with a color /// /// X start position in pixels /// Y start position in pixels @@ -88,15 +89,14 @@ public override void Fill(int x, int y, int width, int height, Color color) } /// - /// Get the pixel color + /// Get the pixel color at a given coordinate /// /// The X pixel position /// The Y pixel position /// The pixel color public override Color GetPixel(int x, int y) - { //comes back as a 4bit value + { var index = GetColorIndexForPixel(x, y); - return IndexedColors[index]; } @@ -113,11 +113,11 @@ public override void SetPixel(int x, int y, Color color) } /// - /// Set the pixel to a shade of gray + /// Set the pixel using a color index /// /// X pixel position /// Y pixel position - /// The color index + /// The color index (0-7) public void SetPixel(int x, int y, int colorIndex) { int index = y * Width / 2 + x / 2; @@ -133,7 +133,7 @@ public void SetPixel(int x, int y, int colorIndex) } /// - /// Invert the pixel + /// Invert the pixel color /// /// x position of pixel /// y position of pixel @@ -143,7 +143,7 @@ public override void InvertPixel(int x, int y) } /// - /// Write a buffer to specific location to the current buffer + /// Write a buffer into this buffer at a specified location /// /// x origin /// y origin @@ -154,14 +154,14 @@ public override void WriteBuffer(int x, int y, IPixelBuffer buffer) x % 2 == 0 && buffer.Width % 2 == 0) { - //we have a happy path + // We can do a direct block copy row by row int sourceIndex, destinationIndex; int length = buffer.Width / 2; for (int i = 0; i < buffer.Height; i++) { sourceIndex = length * i; - destinationIndex = (Width * (y + i) + x) >> 2; //divide by 2 + destinationIndex = (Width * (y + i) + x) >> 2; Array.Copy(buffer.Buffer, sourceIndex, Buffer, destinationIndex, length); } @@ -173,11 +173,11 @@ public override void WriteBuffer(int x, int y, IPixelBuffer buffer) } /// - /// Get the pixel color index + /// Get the pixel's color index (0-7) at a given coordinate /// /// The X pixel position /// The Y pixel position - /// The pixel color as a 4bpp gray value + /// The 4-bit color index public byte GetColorIndexForPixel(int x, int y) { int index = y * Width / 2 + x / 2; @@ -211,15 +211,14 @@ int GetIndexForColor(Color color) for (int i = 0; i < IndexedColors.Length; i++) { - double distance; if (IndexedColors[i] != null) { - distance = GetColorDistance(color, IndexedColors[i]); + double distance = GetColorDistance(color, IndexedColors[i]); if (distance < shortestDistance) { shortestDistance = distance; closestIndex = i; - if (distance == 0) { break; } //perfect match + if (distance == 0) { break; } // perfect match } } }