Save System.Windows.Media.Brush as image file to disk - wpf

I have D3DImage _di that use to draw a background of Wpf Border in the form of a Brush.
The Image is rendered fine but i want to save the Brush to png file on disk even if the Brush is not showed on the View.
I tried as below to save it to disk but all i got is black image:
_receivedBrush =(Brush)new ImageBrush((ImageSource)_di)
RenderTargetBitmap bmpCopied = new RenderTargetBitmap(350, 174, 96, 96, PixelFormats.Default);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
dc.DrawRectangle(_receivedBrush, null, new Rect(new Point(), new Size(350, 174)));
}
bmpCopied.Render(dv);
MemoryStream mse = new MemoryStream();
BmpBitmapEncoder mem = new BmpBitmapEncoder();
mem.Frames.Add(BitmapFrame.Create(bmpCopied));
mem.Save(mse);
File.WriteAllBytes(#"g:\brush.png", mse.ToArray());
mse.Close();
Thanks in advance,

Try changing to PngBitmapEncoder or change the file extension to bmp.

Related

How can I draw a Polyline onto an Image (WPF)

I've tried a few different approaches to this, but can't seem to get a combination that works.
Creating WPF app in C#, Visual Studio.
System.Windows.Shapes.Polyline works really nicely to draw into a Canvas in real-time, but I want to be able to draw in higher resolution onto a non-visual component that I can then render onto an Image.
If I create a Polyline on a Canvas that's visible in the UI, this works fine:
// Make rendertarget size of full page
RenderTargetBitmap rtb = new RenderTargetBitmap((int)wPage, (int)hPage, 96, 96, PixelFormats.Default);
// Render the polyline
rtb.Render(lineVirt);
// Apply to background image
imgBG.Source = rtb;
But if I create a Polyline on a Canvas that's not visible in the UI, then nothing renders to the image. This is probably fair enough. My guess is that the component recognises that it's not visible and therefore doesn't bother to render.
I've considered putting the Canvas somewhere in the UI buried under other controls, but that seems like a horrible kind of hack.
Essentially, all I need is a clean and fast way to draw a multi-point line of a specified width and color onto an Image. I thought that Polyline would work well, but only seems to work in a visible container.
What are my options?
You do not need a rendered Canvas or any other visible Panel at all.
Just use basic drawing primitives available at the Visual layer.
The DrawGeometry method below draws a Geometry onto a BitmapSource, using the bitmap's rendered size, i.e. the size that takes its DPI into account, and returns the resulting BitmapSource.
public static BitmapSource DrawGeometry(
BitmapSource source, Pen pen, Geometry geometry)
{
var visual = new DrawingVisual();
var rect = new Rect(0, 0, source.Width, source.Height);
using (var dc = visual.RenderOpen())
{
dc.DrawImage(source, rect);
dc.DrawGeometry(null, pen, geometry);
}
var target = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
target.Render(visual);
return target;
}
In order to draw in the bitmap's pixel units and hence ignore its DPI, modify the method like this:
var rect = new Rect(0, 0, source.PixelWidth, source.PixelHeight);
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new ImageBrush(source), null, rect);
dc.DrawGeometry(null, pen, geometry);
}
The following method uses the above to draw a polyline as an IEnumerable<Point>.
public static BitmapSource DrawPolyline(
BitmapSource source, Pen pen, IEnumerable<Point> points)
{
var geometry = new PathGeometry();
if (points.Count() >= 2)
{
var figure = new PathFigure { StartPoint = points.First() };
figure.Segments.Add(new PolyLineSegment(points.Skip(1), true));
geometry.Figures.Add(figure);
}
return DrawGeometry(source, pen, geometry);
}
It would be used like
var source = new BitmapImage(new Uri(...));
var pen = new Pen
{
Brush = Brushes.Blue,
Thickness = 2,
};
var points = new List<Point>
{
new Point(100, 100),
new Point(1000, 100),
new Point(1000, 1000),
new Point(100, 1000),
new Point(100, 100),
};
image.Source = DrawPolyline(source, pen, points);
Your canvas needs a size, so someone or something has to Arrange it. That might already be enough to get it to render, but the only reliable way of rendering arbitrary visuals to a bitmap is to actually place them in the visual tree of a window that's displayed and thus laid out by WPF. You can then render to the bitmap in a deferred task at ContextIdle priority to ensure that layout is complete.

WPF: System.Windows.Interop.InteropBitmap to System.Drawing.Bitmap

Is there a way to convert System.Windows.Interop.InteropBitmap to System.Drawing.Bitmap?
Thank you
I was able to solve my problem using the code below. msg.ThumbnailSource contains System.Windows.Interop.InteropBitmap type of object
BitmapSource bmpSource = msg.ThumbnailSource as BitmapSource;
MemoryStream ms = new MemoryStream();
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmpSource));
encoder.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(ms);
You could take the pixels using CopyPixels and use the code from the Bitmap.Lock/Unlock documentation to pass the pixels to the Bitmap.

How to update image file binding to Image control?

My app includes a Image control which has binding to a disk image file. I some condition, the image file need be updated. But the updating can't be done because the image file is open and can not be overwritten. What should I do?
You can try to remove the binding, so the image will not be used by your program
than overwrite the image file
and than re-add the binding
i'm not sure about this, but it's worth a try
Now my solution is:
To use a converter to convert the image path into BitmapImage.
in the converter, load the image using a FileStream and copy the data into a MemoryStream and finally close the FileStream.
BitmapImage bmp = new BitmapImage();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.BeginInit();
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
var memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
memStream.Flush();
fileStream.Close();
bmp.StreamSource = memStream;
bmp.EndInit();
return bmp;

Using RenderTargetBitmap with a Viewport3D

I use Viewport3d and display my 3D Elements with inherited UIElement3D classes... Now I want to make different images of my viewport (different views). That means I have to make a new viewport3d at runtime and apply my specific view for the image on it..
see the following code:
' Start creating Image
Dim bitmap As New RenderTargetBitmap(800,
800,
96,
96,
PixelFormats.Default)
'--------------------
'Render xamlViewport works perfect
bitmap.Render(xamlViewport)
Using stream As FileStream = New FileStream("C:\\temp\\test_xamlviewport.png", FileMode.Create)
Dim encoder As New PngBitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(bitmap))
encoder.Save(stream)
End Using
'---------------------
'Render local Viewport don't work
Dim oViewport As New Viewport3D
oViewport.Height = 800
oViewport.Width = 800
oViewport.Children.Add(New C3DCuboid())
oViewport.Camera = New PerspectiveCamera(New Point3D(4, 7, 6), New Vector3D(-4, -7, -6), New Vector3D(0, 1, 0), 45)
Dim oVisual As New ModelVisual3D
oVisual.Content = New DirectionalLight(Colors.White, New Vector3D(-1, -2, -3))
oViewport.Children.Add(oVisual)
oViewport.Measure(New Size(800, 800))
oViewport.Arrange(New Rect(New Size(800, 800)))
bitmap = New RenderTargetBitmap(800,
800,
96,
96,
PixelFormats.Default)
bitmap.Render(oViewport)
Using stream As FileStream = New FileStream("C:\\temp\\test_localviewport.png", FileMode.Create)
Dim encoder As New PngBitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(bitmap))
encoder.Save(stream)
End Using
The first picture (test_xamlviewport.png) is shown correctly, but the second picture where I create my own Viewport3D Object and print it is empty (test_localviewport.png).
How can I force to Render my UIElement3D Objects or why it doesn't work if i make a new Viewport vs print the existing XAML viewport instance?
Here you find a sample solution which reproduce my problem
http://cid-df67ca3f85229bd1.office.live.com/self.aspx/Development/WpfApplication2.zip
Regards
Roland

Write WPF output to image file

Is there a way I can write the output of WPF say a canvas to a image file, jpg or the like.
I want to use WPF to create a background for me as I want to use the
BitmapEffects for a rectangle and also radius the corners.
I want to use the bitmap in a webpage.
Is this possible?
Malcolm
I have a blog post all about this here. Here's the code from the article:
Rect rect = new Rect(canvas.RenderSize);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
(int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
//encode as PNG
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes("logo.png", ms.ToArray());
Console.WriteLine("Done");

Resources