Hi All,
I have tried to use the DrawSprite method from the C# API but it generates a memory laak and I can’t figure our why… The example from RhinoCommon works like a charm, and mine don’t. The code is below:
public class TestSprite : Command
{
public TestSprite()
{
// Rhino only creates one instance of each command class defined in a
// plug-in, so it is safe to store a refence in a static property.
Instance = this;
}
///<summary>The only instance of this command.</summary>
public static TestSprite Instance
{
get; private set;
}
///<returns>The command name as it appears on the Rhino command line.</returns>
public override string EnglishName
{
get { return "DrawRectangle"; }
}
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
return Test.SpriteDrawing(doc);
}
}
public class Test
{
static float m_sprite_size = 200;
public static Rhino.Commands.Result SpriteDrawing(RhinoDoc doc)
{
var size_option = new Rhino.Input.Custom.OptionDouble(m_sprite_size);
var go = new Rhino.Input.Custom.GetOption();
go.SetCommandPrompt("Sprite drawing mode");
go.AddOptionDouble("Size", ref size_option);
Rhino.Display.DisplayPipeline.PostDrawObjects += DisplayRectangle;
doc.Views.Redraw();
while (go.Get() == Rhino.Input.GetResult.Option)
{
m_sprite_size = (float)size_option.CurrentValue;
doc.Views.Redraw();
}
Rhino.Display.DisplayPipeline.PostDrawObjects -= DisplayRectangle;
return Rhino.Commands.Result.Success;
}
static Rhino.Display.DisplayBitmap mm;
static void DisplayRectangle(object sender, Rhino.Display.DrawEventArgs e)
{
Bitmap bit = new Bitmap((int)m_sprite_size, (int)m_sprite_size);
using (Graphics graphics = Graphics.FromImage(bit))
{
// Main frame
using (Pen pen = new Pen(Color.Black))
{
graphics.FillRectangle(Brushes.White, 0, 0, m_sprite_size, m_sprite_size);
graphics.DrawRectangle(pen, 0, 0, m_sprite_size, m_sprite_size);
}
}
// Display
RectangleF bounds = e.Viewport.Bounds;
mm = new Rhino.Display.DisplayBitmap(bit);
e.Display.DrawSprite(mm, new Point2d(bounds.Width/2 + m_sprite_size / 2, bounds.Height/2 + m_sprite_size/2), m_sprite_size);
bit.Dispose();
}
}
Does anyone has an idea about what’s going on?
Thanks!
Hi,
Thanks for your help, I have actually tried that but does not help… And if you look at the example McNeel provides: http://developer.rhino3d.com/samples/rhinocommon/spritedrawing/
it is how I did it andit works… Very confusing.
I have also tried declaring the DisplayBitmap within my DisplayRectangle method but does not change anything…
Revisting this old thread… I’ve been having the same issues. I followed the McNeel example, but it also seems to have the same memory leak problem when you use a ‘FileSprite’. The issue still seems to occur when creating new DisplayBitmaps. I’ve also tried disposing the displaybitmaps without success.
I am having a similar issue, when trying to make a 2D sketch overlay. I get a memory leak. I have tried creating a new Bitmap, Graphics, and DisplayBitmap, calling e.Display.DrawBitmap, and then calling Dispose on the Bitmap, Graphics, and DisplayBitmap objects, but this still does not fix the problem. I currently think that the problem is with the e.Display.DrawBitmap method itself when inputting new bitmaps to it.
public class DrawBitmapConduit : Rhino.Display.DisplayConduit
{
private DisplayBitmap m_display_bitmap;
public Point2d DrawPt { private get; set; }
public Point2d DrawPtPrev { private get; set; }
public int Width { private get; set; }
public int Height { private get; set; }
Pen colorPen = new Pen(Brushes.Blue, 10);
protected Bitmap flag;
protected Graphics drawGraphics;
public DrawBitmapConduit( RhinoDoc doc)
{
var active_viewport = doc.Views.ActiveView.ActiveViewport;
Width = active_viewport.Size.Width;
Height = active_viewport.Size.Height;
DrawPt = new Point2d(0, 0);
DrawPtPrev = new Point2d(0, 0);
flag = new System.Drawing.Bitmap(Width, Height);
drawGraphics = Graphics.FromImage(flag);
}
protected override void DrawForeground(Rhino.Display.DrawEventArgs e)
{
drawGraphics.FillEllipse(Brushes.Blue, (float)DrawPt.X-5, (float)DrawPt.Y-5, 10, 10);
PointF tmpPt1 = new PointF((float)DrawPtPrev.X, (float)DrawPtPrev.Y);
PointF tmpPt2 = new PointF((float)DrawPt.X,(float)DrawPt.Y);
drawGraphics.DrawLine(colorPen, tmpPt1, tmpPt2);
m_display_bitmap = new DisplayBitmap(flag);
e.Display.DrawBitmap(m_display_bitmap, 0, 0);
DrawPtPrev = DrawPt;
}
}
You are creating a new DisplayBitmap every frame. Either create it in your class constructor or check to see if m_display_bitmap is null and only create one when it is null
Thanks for the quick response! Because this is a interactive sketching application, I need to be able to modify the underlying bitmap image of m_display_bitmap. Is there any way to update the underlying bitmap? so I won’t have to create a new one every frame?
@dale
I did this at the beginning, and several usings were nested, but in addition to the less smooth operation, the memory consumption was still there.
The writing method I mentioned above is to keep the bitmap resident in memory, but only this one Bitmap will be created, and it will not be created frequently.
As soon as I comment out this code, the memory problem is solved immediately
using (var b = new DisplayBitmap(bitmap))
{
e.Display.DrawBitmap(b, 0, 0);
}
Hello, I am currently running into a memory leak when using DisplayBitmap. I have narrowed down the issue to the creation of new DisplayBitmaps. I have tried calling Dispose() like the other forum members above, but still see a memory leak. You say the problem has been fixed in 7.22, but I am using 7.27 and viewing the diassembled .dll in Visual Studio shows that the DisplayBitmap class is still not calling GDI’s DeleteObject() function (as decribed Bitmap.GetHbitmap Method (System.Drawing) | Microsoft Learn) and also noted in c# - WPF CreateBitmapSourceFromHBitmap() memory leak - Stack Overflow. It seems like this is a mistake as the official documentation says delete must be called, RhinoCommon isn’t calling delete (unless it is doing it somewhere we can’t see), and we get a memory leak. Here is an example of test code that will create a memory leak when it shouldn’t (we are calling this function many times). My understanding is that this shouldn’t create a memory leak.
Bitmap bitmap = new Bitmap(e.Width, e.Height, e.Width * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, bufferHandle);
var dp = new DisplayBitmap(bitmap);
bitmap.Dispose();
dp.Dispose();
Getting rid of the “var dp = new DisplayBitmap(bitmap);” line (and corresponding dp.Dispose()) fixes the memory leak.
Thank you for doing that! If it helps, we are calling that code around ~60fps for repro purposes and seeing my whole 32GB of RAM fill up within a minute or two and crashes with:
[ERROR] FATAL UNHANDLED EXCEPTION: System.Runtime.InteropServices.ExternalException (0x80004005): A generic error occurred in GDI+.
at System.Drawing.Bitmap.GetHbitmap(Color background)
at System.Drawing.Bitmap.GetHbitmap()
at Rhino.Display.DisplayBitmap..ctor(Bitmap bitmap)
at Plugin.Rhino.UI.CefSharpManager.onBrowserPaint(Object sender, OnPaintEventArgs e)
at CefSharp.OffScreen.ChromiumWebBrowser.CefSharp.Internals.IRenderWebBrowser.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buffer, Int32 width, Int32 height)
at CefSharp.Internals.RenderClientAdapter.OnPaint(RenderClientAdapter* , scoped_refptr<CefBrowser>* browser, cef_paint_element_type_t type, vector<CefRect\,std::allocator<CefRect> >* dirtyRects, Void* buffer, Int32 width, Int32 height) in C:\projects\cefsharp\CefSharp.Core.Runtime\Internals\RenderClientAdapter.h:line 163
[END ERROR]
Wanted to update this thread with my workaround for this memory leak. I was able to make a C++ module to make a custom CRhinoDisplayConduit and expose a SetBitMap function that I call from the C# side and give it an IntPtr I get from CefSharp. With this strategy I no longer have a memory leak as I manage the C++ bitmap myself.