Skip to content
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

Improve MicroGraphics DrawBuffer bounds checking and behaviour #501

Merged
merged 5 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Source/Meadow.Foundation.Core/Displays/IPixelBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,17 @@ public interface IPixelBuffer
/// Clears the buffer (writes 0s to the byte array)
/// </summary>
void Clear();

/// <summary>
/// Clears a region of the buffer (writes 0s to the byte array)
/// </summary>
/// <param name="originX">The X coord to start</param>
/// <param name="originY">The Y coord to start</param>
/// <param name="width">The width of the region to clear</param>
/// <param name="height">The height of the region to clear</param>
void Clear(int originX, int originY, int width, int height)
{
Fill(originX, originY, width, height, Color.Black);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Meadow.Foundation.Graphics.Buffers;
using Meadow.Peripherals.Displays;
using System;
using static System.Net.Mime.MediaTypeNames;

namespace Meadow.Foundation.Graphics
{
Expand Down Expand Up @@ -955,23 +956,8 @@ public void DrawText(int x, int y, string text,

byte[] bitMap = GetBytesForTextBitmap(text);

if (alignmentH == HorizontalAlignment.Center)
{
x -= MeasureText(text, scaleFactor).Width / 2;
}
else if (alignmentH == HorizontalAlignment.Right)
{
x -= MeasureText(text, scaleFactor).Width;
}

if (alignmentV == VerticalAlignment.Center)
{
y -= MeasureText(text, scaleFactor).Height / 2;
}
else if (alignmentV == VerticalAlignment.Bottom)
{
y -= MeasureText(text, scaleFactor).Height;
}
x = GetXForAlignment(x, MeasureText(text, scaleFactor).Width, alignmentH);
y = GetYForAlignment(y, MeasureText(text, scaleFactor).Height, alignmentV);

DrawBitmap(x, y, bitMap.Length / CurrentFont.Height * 8, CurrentFont.Height, bitMap, scaleFactor);
}
Expand All @@ -983,61 +969,112 @@ public void DrawText(int x, int y, string text,
/// <param name="x">x location of target to draw buffer</param>
/// <param name="y">x location of target to draw buffer</param>
/// <param name="buffer">the source buffer to write to the display buffer</param>
/// /// <param name="rotateBufferForDisplay">rotate the buffer if the display is rotated - maybe be slower</param>
public void DrawBuffer(int x, int y, IPixelBuffer buffer, bool rotateBufferForDisplay = true)
/// <param name="alignmentH">Horizontal alignment: Left, Center or right align the buffer to the x location</param>
/// <param name="alignmentV">Vertical alignment: Top, Center or bottom align the buffer to the y location</param>
public void DrawBuffer(int x, int y, IPixelBuffer buffer,
HorizontalAlignment alignmentH = HorizontalAlignment.Left,
VerticalAlignment alignmentV = VerticalAlignment.Top)
{
x = GetXForAlignment(x, buffer.Width, alignmentH);
y = GetYForAlignment(y, buffer.Height, alignmentV);

DrawBuffer(x, y, buffer);
}

/// <summary>
/// Draw a buffer onto the display buffer at the given location
/// For best performance, source buffer should be the same color depth as the target display
/// </summary>
/// <param name="x">x location of target to draw buffer</param>
/// <param name="y">x location of target to draw buffer</param>
/// <param name="buffer">the source buffer to write to the display buffer</param>
public void DrawBuffer(int x, int y, IPixelBuffer buffer)
{
if(x >= Width || y >= Height || x + buffer.Width < 0 || y + buffer.Height < 0)
{ //nothing to do
return;
}

int xStartIndex = 0;
int yStartIndex = 0;
int widthToDraw = buffer.Width;
int heightToDraw = buffer.Height;

bool isInBounds = true;

if (IgnoreOutOfBoundsPixels)
{
if (x < 0 || y < 0 || x + buffer.Width > Width || y + buffer.Height > Height)
if (x < 0)
{
xStartIndex = 0 - x;
isInBounds = false;
}
if (y < 0)
{
yStartIndex = 0 - x;
isInBounds = false;
}

if(x + buffer.Width > Width)
{
return;
widthToDraw = Width - x;
isInBounds = false;
}

if (y + buffer.Height > Height)
{
heightToDraw = Height - y;
isInBounds = false;
}
}

//fast and happy path
if (Rotation == RotationType.Default)
if (Rotation == RotationType.Default && isInBounds)
{
pixelBuffer.WriteBuffer(x, y, buffer);
}
//rotate buffer if the display is rotated (slow)
else if (rotateBufferForDisplay) //loop over every pixel
else //loop over every pixel
{
for (int i = 0; i < buffer.Width; i++)
for (int i = xStartIndex; i < widthToDraw; i++)
{
for (int j = 0; j < buffer.Height; j++)
for (int j = yStartIndex; j < heightToDraw; j++)
{
pixelBuffer.SetPixel(GetXForRotation(x + i, y + j),
GetYForRotation(x + i, y + j),
buffer.GetPixel(i, j));
}
}
}
//don't rotate buffer with the display (fast)
else
{
pixelBuffer.WriteBuffer(GetXForRotation(x, y), GetYForRotation(x, y), buffer);
}
}

/// <summary>
/// Draw an Image onto the display buffer at the specified location
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="image"></param>
/// <param name="x">x location of target to draw buffer</param>
/// <param name="y">x location of target to draw buffer</param>
/// <param name="image">the source image to write to the display buffer</param>
/// <param name="alignmentH">Horizontal alignment: Left, Center or right align the image to the x location</param>
/// <param name="alignmentV">Vertical alignment: Top, Center or bottom align the image to the y location</param>
public void DrawImage(int x, int y, Image image,
HorizontalAlignment alignmentH = HorizontalAlignment.Left,
VerticalAlignment alignmentV = VerticalAlignment.Top)
=> DrawBuffer(x, y, image.DisplayBuffer, alignmentH, alignmentV);

/// <summary>
/// Draw an Image onto the display buffer at the specified location
/// </summary>
/// <param name="x">x location of target to draw buffer</param>
/// <param name="y">x location of target to draw buffer</param>
/// <param name="image">the source image to write to the display buffer</param>
public void DrawImage(int x, int y, Image image)
{
DrawBuffer(x, y, image.DisplayBuffer);
}
=> DrawBuffer(x, y, image.DisplayBuffer);

/// <summary>
/// Draw an Image onto the display buffer at (0, 0)
/// </summary>
/// <param name="image"></param>
/// <param name="image">the source image to write to the display buffer</param>
public void DrawImage(Image image)
{
DrawImage(0, 0, image);
}
=> DrawImage(0, 0, image);

/// <summary>
/// Draw a text message on the display using the current font
Expand Down Expand Up @@ -1234,6 +1271,24 @@ public virtual void Clear(bool updateDisplay = false)
}
}

/// <summary>
/// Clear a region of the display pixel buffer
/// </summary>
/// <param name="originX">The X coord to start</param>
/// <param name="originY">The Y coord to start</param>
/// <param name="width">The width of the region to clear</param>
/// <param name="height">The height of the region to clear</param>
/// <param name="updateDisplay">Update the display immediately when true</param>
public virtual void Clear(int originX, int originY, int width, int height, bool updateDisplay = false)
{
pixelBuffer.Fill(originX, originY, width, height, display.DisabledColor);

if (updateDisplay)
{
Show();
}
}

/// <summary>
/// Clear the pixel buffer to a color
/// </summary>
Expand Down Expand Up @@ -1390,5 +1445,31 @@ void Fill(int x, int y, int width, int height, Color color)
break;
}
}

int GetXForAlignment(int x, int width, HorizontalAlignment alignmentH)
{
if (alignmentH == HorizontalAlignment.Center)
{
x -= width / 2;
}
else if (alignmentH == HorizontalAlignment.Right)
{
x -= width;
}
return x;
}

int GetYForAlignment(int y, int height, VerticalAlignment alignmentV)
{
if (alignmentV == VerticalAlignment.Center)
{
y -= height / 2;
}
else if (alignmentV == VerticalAlignment.Bottom)
{
y -= height;
}
return y;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public override Task Initialize()
var display = new St7789(
device: Device,
spiBus: spiBus,
chipSelectPin: Device.Pins.D15,
dcPin: Device.Pins.D11,
resetPin: Device.Pins.D14,
chipSelectPin: Device.Pins.A03,
dcPin: Device.Pins.A04,
resetPin: Device.Pins.A05,
width: 240, height: 240, colorMode: ColorType.Format16bppRgb565)
{
};
Expand All @@ -52,43 +52,54 @@ public override Task Initialize()

public override Task Run()
{
var delay = TimeSpan.FromMilliseconds(1000);

graphics.Clear();
graphics.CurrentFont = new Font12x20();

graphics.DrawText(5, 200, "starting...", Color.White);
graphics.Show();
Thread.Sleep(2000);
Thread.Sleep(delay);

int x = 0;
int y = 0;

while (true)
{
DrawImageFromFile(8);
Thread.Sleep(2000);
DrawImageFromResource(8);
Thread.Sleep(2000);
DrawImageFromFile(24);
Thread.Sleep(2000);
DrawImageFromResource(24);
Thread.Sleep(2000);
DrawImageFromFile(8, x, y);
Thread.Sleep(delay);
DrawImageFromResource(8, x, y);
Thread.Sleep(delay);
DrawImageFromFile(24, x, y);
Thread.Sleep(delay);
DrawImageFromResource(24, x, y);
Thread.Sleep(delay);

x += 1;
y += 1;

if (x > 260) x = -20;
if (y > 260) y = -20;
}
}

private void DrawImageFromFile(int depth)
private void DrawImageFromFile(int depth, int x = 0, int y = 0)
{
Console.WriteLine("Showing file...");
var filePath = Path.Combine(MeadowOS.FileSystem.UserFileSystemRoot, $"wl{depth}.bmp");
var image = Image.LoadFromFile(filePath);
graphics.Clear();
graphics.DrawImage(image);
graphics.DrawImage(x, y, image);
graphics.DrawText(5, 200, $"{depth}bpp file", Color.White);
graphics.Show();
}

private void DrawImageFromResource(int depth)
private void DrawImageFromResource(int depth, int x = 0, int y = 0)
{
Console.WriteLine("Showing resource...");
var image = Image.LoadFromResource($"wl{depth}_res.bmp");
graphics.Clear();
graphics.DrawImage(image);
graphics.DrawImage(x, y, image);
graphics.DrawText(5, 200, $"{depth}bpp resource", Color.White);
graphics.Show();
}
Expand Down