diff --git a/Tests/Kernels/GraphicTest/Kernel.cs b/Tests/Kernels/GraphicTest/Kernel.cs index 8cc1ce5c59..5a37de0f71 100644 --- a/Tests/Kernels/GraphicTest/Kernel.cs +++ b/Tests/Kernels/GraphicTest/Kernel.cs @@ -102,6 +102,9 @@ private void DoTest(Canvas aCanvas) aCanvas.DrawImage(bitmap, 10, 10); aCanvas.DrawImage(letter, 50, 10); + Bitmap GetImageTest = aCanvas.GetImage(50, 10, (int)letter.Width, (int)letter.Height); + Assert.AreEqual(GetImageTest.RawData, letter.RawData, "GetImage returns correct values"); + /* Drawing BitmapHeaderV5 image */ Bitmap v5header = new Bitmap(Convert.FromBase64String(parrot)); aCanvas.DrawImage(v5header,0,0); diff --git a/source/Cosmos.Core/ManagedMemoryBlock.cs b/source/Cosmos.Core/ManagedMemoryBlock.cs index 4a6be7d7cf..6daa1aaf42 100644 --- a/source/Cosmos.Core/ManagedMemoryBlock.cs +++ b/source/Cosmos.Core/ManagedMemoryBlock.cs @@ -166,6 +166,26 @@ public unsafe void Copy(MemoryBlock block) MemoryOperations.Copy(xDest, aDataPtr, (int)block.Size); } + /// + /// Copies data from the memory block to the specified array. + /// + /// The start index in the memory block from which to begin copying. + /// The array into which data will be copied. + /// The starting index in the array where data will be copied. + /// The number of elements to copy. + public unsafe void Get(int aStart, int[] aData, int aIndex, int aCount) + { + int* xSrc; + fixed (byte* aArrayPtr = memory) + { + xSrc = (int*)aArrayPtr + aStart; + } + fixed (int* aDataPtr = aData) + { + MemoryOperations.Copy(aDataPtr + aIndex, xSrc, aCount); + } + } + /// /// Write 8-bit to the memory block. /// diff --git a/source/Cosmos.Core/MemoryBlock.cs b/source/Cosmos.Core/MemoryBlock.cs index bed3816176..2838bd404f 100644 --- a/source/Cosmos.Core/MemoryBlock.cs +++ b/source/Cosmos.Core/MemoryBlock.cs @@ -270,6 +270,38 @@ public unsafe void Copy(ManagedMemoryBlock block) MemoryOperations.Copy(xDest, aDataPtr, (int)block.Size); } + /// + /// Copies data from the memory block to the specified array. + /// + /// The byte offset in the memory block from which to start copying. + /// The array into which data will be copied. + /// The starting index in the array where data will be copied. + /// The number of elements to copy. + public unsafe void Get(int aByteOffset, int[] aData, int aIndex, int aCount) + { + int* xSource = (int*)(Base + aByteOffset); + fixed (int* aDataPtr = aData) + { + MemoryOperations.Copy(aDataPtr + aIndex, xSource, aCount); + } + } + + /// + /// Copies a specified number of bytes from the memory block into an array. + /// + /// The byte offset in the memory block from where the copy starts. + /// The array where the data will be copied to. + /// The starting index in the destination array. + /// The number of bytes to copy. + public unsafe void Get(int aByteOffset, byte[] aData, int aIndex, int aCount) + { + byte* xSource = (byte*)(Base + aByteOffset); + fixed (byte* aDataPtr = aData) + { + MemoryOperations.Copy(aDataPtr + aIndex, xSource, aCount); + } + } + /// /// Move bytes array down the memory block. /// diff --git a/source/Cosmos.HAL2/Drivers/Video/VBEDriver.cs b/source/Cosmos.HAL2/Drivers/Video/VBEDriver.cs index 5182cd263a..015ab5c06b 100644 --- a/source/Cosmos.HAL2/Drivers/Video/VBEDriver.cs +++ b/source/Cosmos.HAL2/Drivers/Video/VBEDriver.cs @@ -265,6 +265,18 @@ public uint GetVRAM(uint index) return (uint)pixel; } + /// + /// Get VRAM data. + /// + /// Start position in VRAM. + /// Array to copy data into. + /// Starting index in the array to begin copying data. + /// Number of elements to copy. + public void GetVRAM(int aStart, int[] aData, int aIndex, int aCount) + { + lastbuffer.Get(aStart, aData, aIndex, aCount); + } + /// /// Clear VRAM. /// diff --git a/source/Cosmos.System2/Graphics/Canvas.cs b/source/Cosmos.System2/Graphics/Canvas.cs index a6ac312140..b464eda8da 100644 --- a/source/Cosmos.System2/Graphics/Canvas.cs +++ b/source/Cosmos.System2/Graphics/Canvas.cs @@ -100,6 +100,22 @@ public virtual void Clear(Color color) /// The Y coordinate. public abstract void DrawPoint(Color color, int x, int y); + /// + /// Sets the pixel at the given coordinates to the specified , without unnecessary color operations. + /// + /// The color to draw with (raw argb). + /// The X coordinate. + /// The Y coordinate. + public abstract void DrawPoint(uint color, int x, int y); + + /// + /// Sets the pixel at the given coordinates to the specified . without ToArgb() + /// + /// The color to draw with (raw argb). + /// The X coordinate. + /// The Y coordinate. + public abstract void DrawPoint(int color, int x, int y); + /// /// The name of the Canvas implementation. /// @@ -117,11 +133,14 @@ public virtual void Clear(Color color) /// The X coordinate. /// The Y coordinate. public abstract Color GetPointColor(int x, int y); + /// - /// Gets the index of the pixel at the given coordinates. + /// Gets the color of the pixel at the given coordinates in ARGB. /// /// The X coordinate. /// The Y coordinate. + public abstract int GetRawPointColor(int x, int y); + internal int GetPointOffset(int x, int y) { return (x * Stride) + (y * Pitch); @@ -147,6 +166,47 @@ public virtual void DrawArray(Color[] colors, int x, int y, int width, int heigh } } + /// + /// Draws an array of pixels to the canvas, starting at the given coordinates, + /// using the given width. + /// + /// The pixels to draw. + /// The X coordinate. + /// The Y coordinate. + /// The width of the drawn bitmap. + /// The height of the drawn bitmap. + public virtual void DrawArray(int[] colors, int x, int y, int width, int height) + { + for (int X = 0; X < width; X++) + { + for (int Y = 0; Y < height; Y++) + { + DrawPoint(colors[Y * width + X], x + X, y + Y); + } + } + } + + /// + /// Draws an array of pixels to the canvas, starting at the given coordinates, + /// using the given width. + /// + /// The pixels to draw. + /// The X coordinate. + /// The Y coordinate. + /// The width of the drawn bitmap. + /// The height of the drawn bitmap. + /// int[] colors tarting position + public virtual void DrawArray(int[] colors, int x, int y, int width, int height, int startIndex) + { + for (int X = 0; X < width; X++) + { + for (int Y = 0; Y < height; Y++) + { + DrawPoint(colors[Y * width + X + startIndex], x + X, y + Y); + } + } + } + /// /// Draws a horizontal line. /// @@ -317,6 +377,7 @@ public virtual void DrawCircle(Color color, int xCenter, int yCenter, int radius /// The X center coordinate. /// The Y center coordinate. /// The radius of the circle to draw. + /// Prevents drawing outside the bounds of the canvas. public virtual void DrawFilledCircle(Color color, int x0, int y0, int radius) { int x = radius; @@ -485,40 +546,17 @@ public virtual void DrawSquare(Color color, int x, int y, int size) /// The height of the rectangle. public virtual void DrawRectangle(Color color, int x, int y, int width, int height) { - /* - * we must draw four lines connecting any vertex of our rectangle to do this we first obtain the position of these - * vertex (we call these vertexes A, B, C, D as for geometric convention) - */ - - /* The check of the validity of x and y are done in DrawLine() */ - - /* The vertex A is where x,y are */ - int xa = x; - int ya = y; - - /* The vertex B has the same y coordinate of A but x is moved of width pixels */ - int xb = x + width; - int yb = y; + // Draw top edge from (x, y) to (x + width, y) + DrawLine(color, x, y, x + width, y); - /* The vertex C has the same x coordiate of A but this time is y that is moved of height pixels */ - int xc = x; - int yc = y + height; + // Draw left edge from (x, y) to (x, y + height) + DrawLine(color, x, y, x, y + height); - /* The Vertex D has x moved of width pixels and y moved of height pixels */ - int xd = x + width; - int yd = y + height; + // Draw bottom edge from (x, y + height) to (x + width, y + height) + DrawLine(color, x, y + height, x + width, y + height); - /* Draw a line betwen A and B */ - DrawLine(color, xa, ya, xb, yb); - - /* Draw a line between A and C */ - DrawLine(color, xa, ya, xc, yc); - - /* Draw a line between B and D */ - DrawLine(color, xb, yb, xd, yd); - - /* Draw a line between C and D */ - DrawLine(color, xc, yc, xd, yd); + // Draw right edge from (x + width, y) to (x + width, y + height) + DrawLine(color, x + width, y, x + width, y + height); } /// @@ -569,6 +607,7 @@ public virtual void DrawTriangle(Color color, int v1x, int v1y, int v2x, int v2y /// The image to draw. /// The origin X coordinate. /// The origin Y coordinate. + /// Prevents drawing outside the bounds of the canvas. public virtual void DrawImage(Image image, int x, int y, bool preventOffBoundPixels = true) { Color color; @@ -598,6 +637,35 @@ public virtual void DrawImage(Image image, int x, int y, bool preventOffBoundPix } } + /// + /// Creates a bitmap by copying a portion of your canvas from the specified coordinates and dimensions. + /// + /// The starting X coordinate of the region to copy. + /// The starting Y coordinate of the region to copy. + /// The width of the region to copy. + /// The height of the region to copy. + /// A new containing the copied region. + public virtual Bitmap GetImage(int x, int y, int width, int height) + { + Bitmap bitmap = new Bitmap((uint)x, (uint)y, ColorDepth.ColorDepth32); + + for (int posy = y, desty = 0; posy < y + y; posy++, desty++) + { + for (int posx = x, destx = 0; posx < x + x; posx++, destx++) + { + bitmap.RawData[desty * x + destx] = GetRawPointColor(posx, posy); + } + } + return bitmap; + } + + /// + /// Scales an image to the specified new width and height. + /// + /// The image to be scaled. + /// The width of the scaled image. + /// The height of the scaled image. + /// An array of integers representing the scaled image's pixel data. (Raw bitmap data) static int[] ScaleImage(Image image, int newWidth, int newHeight) { int[] pixels = image.RawData; @@ -607,7 +675,6 @@ static int[] ScaleImage(Image image, int newWidth, int newHeight) int xRatio = (int)((w1 << 16) / newWidth) + 1; int yRatio = (int)((h1 << 16) / newHeight) + 1; int x2, y2; - for (int i = 0; i < newHeight; i++) { for (int j = 0; j < newWidth; j++) @@ -617,7 +684,6 @@ static int[] ScaleImage(Image image, int newWidth, int newHeight) temp[(i * newWidth) + j] = pixels[(y2 * w1) + x2]; } } - return temp; } @@ -629,6 +695,7 @@ static int[] ScaleImage(Image image, int newWidth, int newHeight) /// The Y coordinate. /// The desired width to scale the image to before drawing. /// The desired height to scale the image to before drawing + /// Prevents drawing outside the bounds of the canvas. public virtual void DrawImage(Image image, int x, int y, int w, int h, bool preventOffBoundPixels = true) { Color color; @@ -660,12 +727,39 @@ public virtual void DrawImage(Image image, int x, int y, int w, int h, bool prev } } + /// + /// Draws the given image at the specified coordinates, cropping the image to fit within the maximum width and height. + /// + /// The image to draw. + /// The X coordinate where the image will be drawn. + /// The Y coordinate where the image will be drawn. + /// The maximum width to display the image. If the image exceeds this width, it will be cropped. + /// The maximum height to display the image. If the image exceeds this height, it will be cropped. + /// Prevents drawing outside the bounds of the canvas. + public virtual void CroppedDrawImage(Image image, int x, int y, int maxWidth, int maxHeight, bool preventOffBoundPixels = true) + { + Color color; + int width = Math.Min((int)image.Width, maxWidth); + int height = Math.Min((int)image.Height, maxHeight); + int[] pixels = image.RawData; + + for (int xi = 0; xi < width; xi++) + { + for (int yi = 0; yi < height; yi++) + { + color = Color.FromArgb(pixels[xi + (yi * image.Width)]); + DrawPoint(color, x + xi, y + yi); + } + } + } + /// /// Draws an image with alpha blending. /// /// The image to draw. /// The X coordinate. /// The Y coordinate. + /// Prevents drawing outside the bounds of the canvas. public void DrawImageAlpha(Image image, int x, int y, bool preventOffBoundPixels = true) { Color color; diff --git a/source/Cosmos.System2/Graphics/FullScreenCanvas.cs b/source/Cosmos.System2/Graphics/FullScreenCanvas.cs index 1733e970d2..ca6d5fb417 100644 --- a/source/Cosmos.System2/Graphics/FullScreenCanvas.cs +++ b/source/Cosmos.System2/Graphics/FullScreenCanvas.cs @@ -35,7 +35,7 @@ private enum VideoDriver VGADriver } - static Canvas videoDriver = null; + private static Canvas videoDriver = null; static readonly PCIDevice svgaIIDevice = PCI.GetDevice(VendorID.VMWare, DeviceID.SVGAIIAdapter); /// diff --git a/source/Cosmos.System2/Graphics/SVGAIICanvas.cs b/source/Cosmos.System2/Graphics/SVGAIICanvas.cs index 32a01de9fe..d0e1488d90 100644 --- a/source/Cosmos.System2/Graphics/SVGAIICanvas.cs +++ b/source/Cosmos.System2/Graphics/SVGAIICanvas.cs @@ -17,7 +17,7 @@ public class SVGAIICanvas : Canvas internal Debugger debugger = new("SVGAIIScreen"); static readonly Mode defaultMode = new(1024, 768, ColorDepth.ColorDepth32); - private Mode mode; + Mode mode; private readonly VMWareSVGAII driver; /// @@ -80,23 +80,106 @@ public override void DrawPoint(Color color, int x, int y) driver.SetPixel((uint)x, (uint)y, (uint)color.ToArgb()); } + public override void DrawPoint(uint color, int x, int y) + { + driver.SetPixel((uint)x, (uint)y, color); + } + + public override void DrawPoint(int color, int x, int y) + { + driver.SetPixel((uint)x, (uint)y, (uint)color); + } + + public override void DrawArray(Color[] colors, int x, int y, int width, int height) + { + ThrowIfCoordNotValid(x, y); + ThrowIfCoordNotValid(x + width, y + height); + + for (int i = 0; i < x; i++) + { + for (int ii = 0; ii < y; ii++) + { + DrawPoint(colors[i + (ii * width)], i, ii); + } + } + } + + public override void DrawArray(int[] colors, int x, int y, int width, int height) + { + var frameSize = (int)driver.FrameSize; + + for (int i = 0; i < height; i++) + { + driver.videoMemory.Copy(GetPointOffset(x, y + i) + frameSize, colors, i * width, width); + } + } + + public override void DrawArray(int[] colors, int x, int y, int width, int height, int startIndex) + { + var frameSize = (int)driver.FrameSize; + + for (int i = 0; i < height; i++) + { + driver.videoMemory.Copy(GetPointOffset(x, y + i) + frameSize, colors, i * width + startIndex, width); + } + } + public override void DrawFilledRectangle(Color color, int xStart, int yStart, int width, int height, bool preventOffBoundPixels = true) { var argb = color.ToArgb(); var frameSize = (int)driver.FrameSize; - if(preventOffBoundPixels) + if (preventOffBoundPixels) { width = Math.Min(width, (int)mode.Width - xStart); height = Math.Min(height, (int)mode.Height - yStart); } - - for (int i = yStart; i < yStart + height; i++) { driver.videoMemory.Fill(GetPointOffset(xStart, i) + (int)frameSize, width, argb); } } + public override void DrawRectangle(Color color, int x, int y, int width, int height) + { + if (color.A < 255) + { + // Draw top edge from (x, y) to (x + width, y) + DrawLine(color, x, y, x + width, y); + // Draw left edge from (x, y) to (x, y + height) + DrawLine(color, x, y, x, y + height); + // Draw bottom edge from (x, y + height) to (x + width, y + height) + DrawLine(color, x, y + height, x + width, y + height); + // Draw right edge from (x + width, y) to (x + width, y + height) + DrawLine(color, x + width, y, x + width, y + height); + } + else + { + int rawColor = color.ToArgb(); + // Draw top edge from (x, y) to (x + width, y) + for (int posX = x; posX < x + width; posX++) + { + DrawPoint((uint)rawColor, posX, y); + } + // Draw left edge from (x, y) to (x, y + height) + int newY = y + height; + for (int posX = x; posX < x + width; posX++) + { + DrawPoint((uint)rawColor, posX, newY); + } + // Draw bottom edge from (x, y + height) to (x + width, y + height) + for (int posY = y; posY < y + height; posY++) + { + DrawPoint((uint)rawColor, x, posY); + } + // Draw right edge from (x + width, y) to (x + width, y + height) + int newX = x + width; + for (int posY = y; posY < y + height; posY++) + { + DrawPoint((uint)rawColor, newX, posY); + } + } + } + //public override IReadOnlyList AvailableModes { get; } = new List /// /// Available SVGA 2 supported video modes. @@ -315,6 +398,27 @@ public override Color GetPointColor(int x, int y) return Color.FromArgb((int)driver.GetPixel((uint)x, (uint)y)); } + public override int GetRawPointColor(int x, int y) + { + return (int)driver.GetPixel((uint)x, (uint)y); + } + + public override Bitmap GetImage(int x, int y, int width, int height) + { + var frameSize = (int)driver.FrameSize; + int[] buffer = new int[width]; + int[] all = new int[width * height]; + for (int i = 0; i < height; i++) + { + driver.videoMemory.Get(GetPointOffset(x, y + i) + frameSize, buffer, 0, width); + buffer.CopyTo(all, width * i); + } + Bitmap toReturn = new Bitmap((uint)width, (uint)height, ColorDepth.ColorDepth32); + toReturn.RawData = all; + + return toReturn; + } + public override void Display() { driver.DoubleBufferUpdate(); @@ -357,14 +461,61 @@ public override void DrawImage(Image image, int x, int y, bool preventOffBoundPi var height = (int)image.Height; var frameSize = (int)driver.FrameSize; var data = image.RawData; + if (preventOffBoundPixels) { var maxWidth = Math.Min(width, (int)mode.Width - x); var maxHeight = Math.Min(height, (int)mode.Height - y); + var startX = Math.Max(0, x); + var startY = Math.Max(0, y); + + var sourceX = Math.Max(0, -x); + var sourceY = Math.Max(0, -y); + + // Adjust maxWidth and maxHeight if startX or startY were changed + maxWidth -= startX - x; + maxHeight -= startY - y; for (int i = 0; i < maxHeight; i++) { - driver.videoMemory.Copy(GetPointOffset(x, y + i) + frameSize, data, i * width, maxWidth); + int sourceIndex = (sourceY + i) * width + sourceX; + driver.videoMemory.Copy(GetPointOffset(startX, startY + i) + frameSize, data, sourceIndex, maxWidth); + } + } + else + { + for (int i = 0; i < height; i++) + { + driver.videoMemory.Copy(GetPointOffset(x, y + i) + frameSize, data, i * width, width); + } + } + } + + public override void CroppedDrawImage(Image image, int x, int y, int width, int height, bool preventOffBoundPixels = true) + { + var frameSize = (int)driver.FrameSize; + var data = image.RawData; + + if (preventOffBoundPixels) + { + var modeWidth = (int)mode.Width; + var modeHeight = (int)mode.Height; + + var maxWidth = Math.Min(width, modeWidth - x); + var maxHeight = Math.Min(height, modeHeight - y); + + var startX = Math.Max(0, -x); + var startY = Math.Max(0, -y); + + var sourceWidth = maxWidth - startX; + var sourceHeight = maxHeight - startY; + + for (int i = 0; i < sourceHeight; i++) + { + int destY = y + startY + i; + int destOffset = GetPointOffset(x + startX, destY) + frameSize; + + driver.videoMemory.Copy(destOffset, data, (startY + i) * width + startX, sourceWidth); } } else diff --git a/source/Cosmos.System2/Graphics/VBECanvas.cs b/source/Cosmos.System2/Graphics/VBECanvas.cs index bb86dbf1be..493732cbf4 100644 --- a/source/Cosmos.System2/Graphics/VBECanvas.cs +++ b/source/Cosmos.System2/Graphics/VBECanvas.cs @@ -14,7 +14,7 @@ namespace Cosmos.System.Graphics public class VBECanvas : Canvas { static readonly Mode defaultMode = new(1024, 768, ColorDepth.ColorDepth32); - readonly VBEDriver driver; + private readonly VBEDriver driver; Mode mode; /// @@ -234,6 +234,39 @@ public override void DrawPoint(Color aColor, int aX, int aY) } } + public override void DrawPoint(uint aColor, int aX, int aY) + { + uint offset; + + switch (Mode.ColorDepth) + { + case ColorDepth.ColorDepth32: + offset = (uint)GetPointOffset(aX, aY); + + driver.SetVRAM(offset, (byte)((aColor >> 16) & 0xFF)); + driver.SetVRAM(offset + 1, (byte)((aColor >> 8) & 0xFF)); + driver.SetVRAM(offset + 2, (byte)(aColor & 0xFF)); + driver.SetVRAM(offset + 3, (byte)((aColor >> 24) & 0xFF)); + + break; + case ColorDepth.ColorDepth24: + offset = (uint)GetPointOffset(aX, aY); + + driver.SetVRAM(offset, (byte)((aColor >> 16) & 0xFF)); + driver.SetVRAM(offset + 1, (byte)((aColor >> 8) & 0xFF)); + driver.SetVRAM(offset + 2, (byte)(aColor & 0xFF)); + + break; + default: + throw new NotImplementedException("Drawing pixels with color depth " + (int)Mode.ColorDepth + " is not yet supported."); + } + } + + public override void DrawPoint(int aColor, int aX, int aY) + { + DrawPoint((uint)aColor, aX, aY); + } + public override void DrawArray(Color[] aColors, int aX, int aY, int aWidth, int aHeight) { ThrowIfCoordNotValid(aX, aY); @@ -248,43 +281,159 @@ public override void DrawArray(Color[] aColors, int aX, int aY, int aWidth, int } } + public override void DrawArray(int[] aColors, int aX, int aY, int aWidth, int aHeight) + { + for (int i = 0; i < aHeight; i++) + { + if (i >= mode.Height) + { + return; + } + int destinationIndex = (aY + i) * (int)mode.Width + aX; + driver.CopyVRAM(destinationIndex, aColors, i * aWidth, aWidth); + } + } + + public override void DrawArray(int[] aColors, int aX, int aY, int aWidth, int aHeight, int startIndex) + { + for (int i = 0; i < aHeight; i++) + { + if (i >= mode.Height) + { + return; + } + int destinationIndex = (aY + i) * (int)mode.Width + aX; + driver.CopyVRAM(destinationIndex, aColors, i * aWidth + startIndex, aWidth); + } + } + public override void DrawFilledRectangle(Color aColor, int aX, int aY, int aWidth, int aHeight, bool preventOffBoundPixels = true) { // ClearVRAM clears one uint at a time. So we clear pixelwise not byte wise. That's why we divide by 32 and not 8. - if(preventOffBoundPixels) - aWidth = (int)(Math.Min(aWidth, Mode.Width - aX) * (int)Mode.ColorDepth / 32); + if (preventOffBoundPixels) + { + aWidth = (int)(Math.Min(aWidth, Mode.Width - aX) * (int)Mode.ColorDepth / 32); + } var color = aColor.ToArgb(); - for (int i = aY; i < aY + aHeight; i++) { driver.ClearVRAM(GetPointOffset(aX, i), aWidth, color); } } - public override void DrawImage(Image aImage, int aX, int aY, bool preventOffBoundPixels = true) + public override void DrawRectangle(Color color, int x, int y, int width, int height) { - var xBitmap = aImage.RawData; - var xWidth = (int)aImage.Width; - var xHeight = (int)aImage.Height; + if (color.A < 255) + { + // Draw top edge from (x, y) to (x + width, y) + DrawLine(color, x, y, x + width, y); + // Draw left edge from (x, y) to (x, y + height) + DrawLine(color, x, y, x, y + height); + // Draw bottom edge from (x, y + height) to (x + width, y + height) + DrawLine(color, x, y + height, x + width, y + height); + // Draw right edge from (x + width, y) to (x + width, y + height) + DrawLine(color, x + width, y, x + width, y + height); + } + else + { + int rawColor = color.ToArgb(); + // Draw top edge from (x, y) to (x + width, y) + for (int posX = x; posX < x + width; posX++) + { + DrawPoint((uint)rawColor, posX, y); + } + // Draw left edge from (x, y) to (x, y + height) + int newY = y + height; + for (int posX = x; posX < x + width; posX++) + { + DrawPoint((uint)rawColor, posX, newY); + } + // Draw bottom edge from (x, y + height) to (x + width, y + height) + for (int posY = y; posY < y + height; posY++) + { + DrawPoint((uint)rawColor, x, posY); + } + // Draw right edge from (x + width, y) to (x + width, y + height) + int newX = x + width; + for (int posY = y; posY < y + height; posY++) + { + DrawPoint((uint)rawColor, newX, posY); + } + } + } + + public override void DrawImage(Image image, int x, int y, bool preventOffBoundPixels = true) + { + var width = (int)image.Width; + var height = (int)image.Height; + var data = image.RawData; + if (preventOffBoundPixels) { - var maxWidth = Math.Min(xWidth, (int)mode.Width - aX); - var maxHeight = Math.Min(xHeight, (int)mode.Height - aY); - int xOffset = aY * (int)Mode.Width + aX; + var maxWidth = Math.Min(width, (int)mode.Width - x); + var maxHeight = Math.Min(height, (int)mode.Height - y); + var startX = Math.Max(0, x); + var startY = Math.Max(0, y); + + var sourceX = Math.Max(0, -x); + var sourceY = Math.Max(0, -y); + + // Adjust maxWidth and maxHeight if startX or startY were changed + maxWidth -= startX - x; + maxHeight -= startY - y; + for (int i = 0; i < maxHeight; i++) { - driver.CopyVRAM((i * (int)Mode.Width) + xOffset, xBitmap, i * xWidth, maxWidth); + int sourceIndex = (sourceY + i) * width + sourceX; + int destinationIndex = (startY + i) * (int)mode.Width + startX; + driver.CopyVRAM(destinationIndex, data, sourceIndex, maxWidth); } } else { - int xOffset = aY * xHeight + aX; - for (int i = 0; i < Mode.Height; i++) + for (int i = 0; i < height; i++) { - driver.CopyVRAM((i * (int)Mode.Width) + xOffset, xBitmap, i * xWidth, xWidth); + int destinationIndex = (y + i) * (int)mode.Width + x; + driver.CopyVRAM(destinationIndex, data, i * width, width); } } + } + + public override void CroppedDrawImage(Image aImage, int aX, int aY, int aWidth, int aHeight, bool preventOffBoundPixels = true) + { + var xBitmap = aImage.RawData; + var xWidth = aWidth; + var xHeight = aHeight; + + if (preventOffBoundPixels) + { + var maxWidth = Math.Min(xWidth, (int)Mode.Width - aX); + var maxHeight = Math.Min(xHeight, (int)Mode.Height - aY); + + var startX = Math.Max(0, aX); + var startY = Math.Max(0, aY); + var sourceX = Math.Max(0, -aX); + var sourceY = Math.Max(0, -aY); + + maxWidth -= startX - aX; + maxHeight -= startY - aY; + + for (int i = 0; i < maxHeight; i++) + { + int sourceIndex = (sourceY + i) * xWidth + sourceX; + int destinationIndex = (startY + i) * (int)Mode.Width + startX; + driver.CopyVRAM(destinationIndex, xBitmap, sourceIndex, maxWidth); + } + } + else + { + for (int i = 0; i < xHeight; i++) + { + int destinationIndex = (aY + i) * (int)Mode.Width + aX; + driver.CopyVRAM(destinationIndex, xBitmap, i * xWidth, xWidth); + } + } } #endregion @@ -302,6 +451,38 @@ public override Color GetPointColor(int aX, int aY) return Color.FromArgb((int)driver.GetVRAM(offset)); } + public override int GetRawPointColor(int aX, int aY) + { + uint offset = (uint)GetPointOffset(aX, aY); + return (int)driver.GetVRAM(offset); + } + + public override Bitmap GetImage(int x, int y, int width, int height) + { + Bitmap bitmap = new((uint)width, (uint)height, ColorDepth.ColorDepth32); + + int startX = Math.Max(0, x); + int startY = Math.Max(0, y); + int endX = Math.Min(x + width, (int)Mode.Width); + int endY = Math.Min(y + height, (int)Mode.Height); + + int offsetX = Math.Max(0, -x); + int offsetY = Math.Max(0, -y); + + int[] rawData = new int[width * height]; + + for (int posy = startY; posy < endY; posy++) + { + int srcOffset = posy * (int)Mode.Width + startX; + int destOffset = (posy - startY + offsetY) * width + offsetX; + + driver.GetVRAM(srcOffset, rawData, destOffset, endX - startX); + } + + bitmap.RawData = rawData; + return bitmap; + } + #endregion } diff --git a/source/Cosmos.System2/Graphics/VGACanvas.cs b/source/Cosmos.System2/Graphics/VGACanvas.cs index 2157e06dee..9cc89fbd40 100644 --- a/source/Cosmos.System2/Graphics/VGACanvas.cs +++ b/source/Cosmos.System2/Graphics/VGACanvas.cs @@ -12,7 +12,7 @@ namespace Cosmos.System.Graphics public class VGACanvas : Canvas { bool enabled; - readonly VGADriver driver; + private readonly VGADriver driver; /// /// Available VGA supported video modes. @@ -96,16 +96,48 @@ public override void DrawFilledRectangle(Color aColor, int aXStart, int aYStart, driver.DrawFilledRectangle(aXStart, aYStart, aWidth, aHeight, driver.GetClosestColorInPalette(aColor)); } + public override void DrawRectangle(Color color, int x, int y, int width, int height) + { + int rawColor = color.ToArgb(); + /* Draw the top edge */ + for (int posX = x; posX < x + width; posX++) + { + DrawPoint((uint)rawColor, posX, y); + } + /* Draw the bottom edge */ + int newY = y + height; + for (int posX = x; posX < x + width; posX++) + { + DrawPoint((uint)rawColor, posX, newY); + } + /* Draw the left edge */ + for (int posY = y; posY < y + height; posY++) + { + DrawPoint((uint)rawColor, x, posY); + } + /* Draw the right edge */ + int newX = x + width; + for (int posY = y; posY < y + height; posY++) + { + DrawPoint((uint)rawColor, newX, posY); + } + } + public override void DrawPoint(Color aColor, int aX, int aY) { driver.SetPixel((uint)aX, (uint)aY, aColor); } - public void DrawPoint(uint aColor, int aX, int aY) + public override void DrawPoint(uint aColor, int aX, int aY) { driver.SetPixel((uint)aX, (uint)aY, aColor); } + public override void DrawPoint(int aColor, int aX, int aY) + { + driver.SetPixel((uint)aX, (uint)aY, (uint)aColor); + } + public override List AvailableModes => availableModes; public override Color GetPointColor(int aX, int aY) @@ -113,6 +145,11 @@ public override Color GetPointColor(int aX, int aY) return Color.FromArgb((int)driver.GetPixel((uint)aX, (uint)aY)); } + public override int GetRawPointColor(int aX, int aY) + { + return (int)driver.GetPixel((uint)aX, (uint)aY); + } + public override Mode DefaultGraphicsMode => new Mode(640, 480, ColorDepth.ColorDepth4); ///