-
Notifications
You must be signed in to change notification settings - Fork 987
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
Implement CachedBitmap functionality in System.Drawing #8822
Comments
Tagging subscribers to this area: @safern, @tannergooding |
Agreed, this would be really useful |
I've implemented this on my own fork, with tests and doc comments: https://github.com/reflectronic/runtime/tree/cachedbitmap As for performance, I have a benchmark comparing drawing with Bitmap and CachedBitmap: Benchmarkusing BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
namespace ConsoleApp1
{
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class DrawingBenchmark
{
Image surface;
Graphics graphics;
Bitmap bitmap;
CachedBitmap cachedBitmap;
[GlobalSetup]
public void GlobalSetup()
{
bitmap = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Image"));
surface = new Bitmap(bitmap.Width, bitmap.Height);
graphics = Graphics.FromImage(surface);
cachedBitmap = new CachedBitmap(bitmap, graphics);
}
[Benchmark]
public void DrawBitmap()
{
graphics.DrawImage(bitmap, 0, 0);
}
[Benchmark]
public void DrawCachedBitmap()
{
graphics.DrawCachedBitmap(cachedBitmap, 0, 0);
}
}
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<DrawingBenchmark>();
}
}
}
As you can see, CachedBitmap is a pretty simple optimization that can bring a lot of value in various scenarios. |
Well thought out, 👍 |
I think overall the proposed API makes sense and matches the GDI+ API exposed. It might be interesting to see what would be required to add support to libgdiplus. @safern, if you don't have any concerns, I think this could be marked "ready for review". |
Yeah I think we should figure out the correct story in Unix for this... The problem I see is that
I think that could work as well for Also, should implement Overall looks good other than the Also, just to have it in the issue info, WinForms already exposes a |
I think you are thinking of WPF's |
namespace System.Drawing.Imaging
{
+ public sealed class CachedBitmap : MarshalByRefObject, IDisposable
+ {
+ public CachedBitmap(Bitmap bitmap, Graphics graphics);
+ public void Dispose();
+ }
}
namespace System.Drawing
{
public sealed class Graphics : MarshalByRefObject, IDisposable, IDeviceContext
{
+ public void DrawCachedBitmap(CachedBitmap cachedBitmap, int x, int y);
}
} |
@reflectronic interested in putting up a PR? |
Yup, you can assign me. Right now, I am working on getting it implemented in libgdiplus as well. Hopefully, that can get reviewed and merged quickly once I put that up. As for the versioning problem, it looks like libgdiplus exposes the version with |
|
Btw, I set the milestone to future as there's no rush to implement this, but if we're able to get it in the next couple of weeks and make it into 5.0, great 😄 |
@terrajobst The API you copy/pasted included the MarshalByRefObject base class. Didn't we say no to that part? |
I opened a pull request for the libgdiplus implementation, if anybody wants to take a look mono/libgdiplus#654 |
@reflectronic it's been a while, but are you interested in offering a PR? It's not clear from above. If not, we can mark up for grabs. As you probably know, we have removed the Unix implementation of Drawing. |
I would certainly like to offer a PR, but I think this needs to go through API review again, since #8833 missed this API. So, we would need something like: namespace System.Drawing.Imaging
{
public sealed class CachedBitmap : IDisposable
{
public CachedBitmap(Bitmap bitmap, Graphics graphics);
+ public IntPtr Handle { get; }
+ public static CachedBitmap FromHandle(IntPtr handle);
public void Dispose();
}
}
namespace System.Drawing
{
public sealed class Graphics : MarshalByRefObject, IDisposable, IDeviceContext
{
public void DrawCachedBitmap(CachedBitmap cachedBitmap, int x, int y);
}
} Since this has to go through API review again, I also think we should change the namespace... namespace System.Drawing.Drawing2D
{
+ public sealed class CachedBitmap : IDisposable
+ {
+ public CachedBitmap(Bitmap bitmap, Graphics graphics);
+ public IntPtr Handle { get; }
+ public static CachedBitmap FromHandle(IntPtr handle);
+ public void Dispose();
+ }
}
namespace System.Drawing
{
public sealed class Graphics : MarshalByRefObject, IDisposable, IDeviceContext
{
+ public void DrawCachedBitmap(CachedBitmap cachedBitmap, int x, int y);
}
} Does that look good to you? I don't know who the area owners are, since Santi left and I can't see who's in @dotnet/area-system-drawing. It would also be nice to have a status update re: triaging the other APIs proposed for System.Drawing... they are pretty straightforward and I think can be flipped ready-for-review pretty easily. |
@reflectronic I'm going to implement this as is to get this moving. I think there are reasonable arguments for both namespaces. The native handle APIs can be spun out into a separate API proposal when someone has a pressing need for it. |
Implements GDI+ `CachedBitmap` wrapper which allows caching a device dependent copy of `Bitmap`. Rendering is signficantly faster (5x+), although it has a few caveats: - It needs to be regenerated if color depth changes - It cannot be rendered to a Graphics object that has a tranform other than translation (no rotation, scaling) Fixes dotnet#8822
Implements GDI+ `CachedBitmap` wrapper which allows caching a device dependent copy of `Bitmap`. Rendering is signficantly faster (5x+), although it has a few caveats: - It needs to be regenerated if color depth changes - It cannot be rendered to a Graphics object that has a tranform other than translation (no rotation, scaling) Fixes dotnet#8822
Implements GDI+ `CachedBitmap` wrapper which allows caching a device dependent copy of `Bitmap`. Rendering is signficantly faster (5x+), although it has a few caveats: - It needs to be regenerated if color depth changes - It cannot be rendered to a Graphics object that has a tranform other than translation (no rotation, scaling) Fixes #8822
See discussion here: dotnet/winforms#8822 It doesn't belong here /cc @reflectronic @JeremyKuhne
Background
Drawing a
Bitmap
in GDI+ has relatively poor performance, since the image is stored in a device-independent format. TheCachedBitmap
class stores an image in a format that is optimized for display on a particular device. Thus, rendering an image stored in aCachedBitmap
is fast, because no processing time is spent converting the image to the format required by the display device. Naturally, such a capability can substantially improve graphics performance in many scenarios. However, for whatever reason, it's not directly usable from theSystem.Drawing
APIs.API Proposal
This API is derived from this page of the GDI+ flat API. (I don't know what
GdipEmfToWmfBits
is doing on that page, butSystem.Drawing
doesn't support saving metafiles anyway.)As an aside: currently, libgdiplus doesn't implement this functionality. I imagine it would be fine to just PNSE when not on Windows for now (or, alternatively, making it "lie" about being a CachedBitmap, and simply calling
DrawImage
fromDrawCachedBitmap
).The text was updated successfully, but these errors were encountered: