Using vectorised API's to do basic drawing instead of calling out to GDI+.
- Dominant language
- C#
- Stars
- 4.9k
- Forks
- 1.1k
- Avg merge
- 20h 23m
- Merged PRs (30d)
- 103
Description
### Background and motivation
All calls to draw any kind of graphics, even just clearing a Bitmap to a certain colour, will result in a GDI/GDI+ invocation.
Anecdotal testing shows that using vectorised API's like AVX/SSE to directly modify the underlying Bitmap data displayed significantly improved performance. I imagine this is due to the GDI libraries being old and not being vectorised themselves.
For example; to clear a Bitmap, instead of calling `Graphics.Clear(color)`, you can locate the Bitmap in memory using `LockBits`, create a stack allocated integer containing `color`, and use `Span.Fill(color)`, this won't call out to GDI+, and Span uses vectorised instructions to accelerate the clearing of the pixels.
Code example:
```
///
/// Fills the entire drawing surface with the specified color.
///
public unsafe void Clear(Color color)
{
// Store the height so that we don't make two calls to Bitmap.Height, saves 1 P/Invoke.
int height = bitmap.Height;
var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, height), ImageLockMode.WriteOnly, imageBitmap.PixelFormat);
// This is the length of the underlying memory which backs this bitmap.
int lengthInBytes = bmpData.Stride * height;
// Get our color as an integer - There might be a quicker way to do this, although this is already fast using the stack and an 'unsafe' type conversion.
byte* colorData = stackalloc byte[4] { color.B, color.G, color.R, color.A };
int colorInt = Unsafe.As(ref *colorData);
// Get the backing memory of the Bitmap as a Span.
Span imgBytes = new Span((void*)bmpData.Scan0, lengthInBytes / sizeof(int));
// Use the accelerated Span.Fill function to clear the memory with the requested color.
imgBytes.Fill(colorInt);
// Release the memory.
bitmap.UnlockBits(bmpData);
}
```
This is just one example of the basic `Clear` functionality, I have also experimented with drawing another bitmap directly into the memory of the source bitmap using AVX/SSE, this drastically improves draw performance (increasing as the number of draw calls increases), you can also choose to draw with transparency on each draw call, rather than dictating transparency support in the PixelFormat.
If this kind of proposal has support I am more than willing to provide PRs to add this to Bitmap.cs
### API Proposal
```
public sealed unsafe class Bitmap : Image, IPointer
{
public unsafe void Clear(Color color);
public unsafe void Draw(Bitmap otherBitmap, Rectangle bounds, bool blendPixels);
}
```
### API Usage
```csharp
Bitmap bitmap = new Bitmap(1000, 1000);
bitmap.Clear(Color.Red);
```
### Alternative Designs
_No response_
### Risks
_No response_
### Will this feature affect UI controls?
No
Contributor guide
Assessment
This issue has not been assessed yet.