Frederik Hoeft
Frederik Hoeft

Reputation: 1460

Drawing a geometry onto a Bitmap (Avalonia)

My question is pretty straight forward: Is it possible to draw a Avalonia.Media.Geometry onto a Avalonia.Media.Bitmap so that I can save the resulting Image to a Steam as well as use the whole thing with an ImageBrush?

In the System.Drawing namespace one could simply call Graphics.FromImage(mySystemDrawingBitmap); to get a Graphics object which can then be used to draw whatever geometry you want.

Is there a similar easy approach to this in Avalonia? I dug up something called RenderTargetBitmap from the documentation which looks promising as it has a method called CreateDrawingContext but it apparently needs an IVisualBrushRenderer (whatever that may be) instance as a parameter to work. There seems to be a thing called ImmediateRenderer that implements said interface however it requires an Avalonia.VisualTree.IVisual itself as a parameter in it's constructor and at this stage I'm kinda doubting the usefulness of the RenderTargetBitmap for my problem as it seems to have a massive overhead of randomly required object instances I don't really want or need and I'm not going to create a dummy IVisual instance just to draw something on a bitmap. This just has a horrible code smell to it.

Sooo: Rendering a Avalonia.Media.Geometry (created with Geometry.Parse() for example) onto a Avalonia.Media.Bitmap. How are you actually supposed to do it?

Upvotes: 1

Views: 2933

Answers (2)

Xanatos
Xanatos

Reputation: 445

Since the accepted answer was throwing a deprecated warning for me I rewrote the code like this.

public Bitmap? CreateBitmap(StreamGeometry streamGeometry, Brush brush)
{
    var testImage = new DrawingImage
    {
        Drawing = new GeometryDrawing
        {
            Geometry = streamGeometry,
            Brush = brush
        }
    };
    var pixelSize = new PixelSize((int)testImage.Size.Width, (int)testImage.Size.Height);
    Bitmap? returnImage = null;
    using (MemoryStream memoryStream = new())
    {
        using (RenderTargetBitmap bitmap = new(pixelSize, new Vector(72, 72)))
        {
            using (DrawingContext ctx = new(bitmap.CreateDrawingContext(null)))
            {
                testImage.Drawing.Draw(ctx);
            }
            bitmap.Save(memoryStream);
        }
        memoryStream.Seek(0, SeekOrigin.Begin);
        returnImage = new Bitmap(memoryStream);
    }
    return returnImage;
}

It is working for Avalonia 0.10.18 maybe it will break with the 11.0.0 release.

Hopefully this will help someone.

It did break with 11.0.0 so here is an updated version. The DrawingContext creation did get simplified.

public Bitmap? CreateBitmap(StreamGeometry streamGeometry, Brush brush)
{
    var internalImage = new DrawingImage
    {
        Drawing = new GeometryDrawing
        {
            Geometry = streamGeometry,
            Brush = brush
        }
    };
    var pixelSize = new PixelSize((int)internalImage.Size.Width, (int)internalImage.Size.Height);
    Bitmap? returnImage = null;
    using (MemoryStream memoryStream = new())
    {
        using (RenderTargetBitmap bitmap = new(pixelSize, new Vector(72, 72)))
        {
            using (DrawingContext ctx = bitmap.CreateDrawingContext())
            {
                internalImage.Drawing.Draw(ctx);
            }
            bitmap.Save(memoryStream);
        }
        memoryStream.Seek(0, SeekOrigin.Begin);
        returnImage = new Bitmap(memoryStream);
    }
    return returnImage;
}

Upvotes: 1

kekekeks
kekekeks

Reputation: 3623

void RenderToFile(Geometry geometry, Brush brush, string path)
{
    var control = new DrawingPresenter()
    {
        Drawing = new GeometryDrawing
        {
            Geometry = geometry, Brush = brush,
        },
        Width = geometry.Bounds.Right,
        Height = geometry.Bounds.Bottom
    };

    RenderToFile(control, path);
}

void RenderToFile(Control target, string path)
{
    var pixelSize = new PixelSize((int) target.Width, (int) target.Height);
    var size = new Size(target.Width, target.Height);
    using (RenderTargetBitmap bitmap = new RenderTargetBitmap(pixelSize, new Vector(96, 96)))
    {
        target.Measure(size);
        target.Arrange(new Rect(size));
        bitmap.Render(target);
        bitmap.Save(path);
    }
}

Upvotes: 2

Related Questions