I've drawn ellipse over canvas, now how can I save it as an image. I know you cannot directly save canvas as an image and neither you can take screenshot. I am working in C#/xaml. Below is my code for drawing ellipse over canvas.
private void canvasDraw_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (drawing)
{
PointerPoint current = e.GetCurrentPoint((UIElement)sender);
// Line line = new Line() { X1 = start.Position.X, Y1 = start.Position.Y, X2 = current.Position.X, Y2 = current.Position.Y };
//line.Stroke = new SolidColorBrush(Colors.Black);
Ellipse circle = new Ellipse();
circle.SetValue(Canvas.LeftProperty, current.Position.X);
circle.SetValue(Canvas.TopProperty, current.Position.Y);
circle.Height = 20;
circle.Width = 20;
circle.Fill = currentBrush;
circle.Opacity = 0.7;
circle.SetValue(Canvas.ZIndexProperty,1);
canvasDraw.Children.Add(circle);
}
}
Edit : I am able to save the image by using InkManager. I stored every Ellipse in inkmanager and called SaveAsync method but the final issue is the image comes in black for example if I draw red ellipse the saved image has black ellipse.
Read this example http://blogs.msdn.com/b/swick/archive/2007/12/02/rendering-ink-and-image-to-a-bitmap-using-wpf.aspx
Code Extract from site:
// render InkCanvas' visual tree to the RenderTargetBitmap
RenderTargetBitmap targetBitmap =
new RenderTargetBitmap((int)inkCanvas1.ActualWidth,
(int)inkCanvas1.ActualHeight,
96d, 96d,
PixelFormats.Default);
targetBitmap.Render(inkCanvas1);
// add the RenderTargetBitmap to a Bitmapencoder
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(targetBitmap));
// save file to disk
FileStream fs = File.Open(fileName, FileMode.OpenOrCreate);
encoder.Save(fs);
Yes RenderTargetBitmap it is not available in windows store app target to 8.0
Windows 8.1 APIs include the new RenderTargetBitmap class that allows to render to a bitmap with its RenderAsync methods.
you can now use this method
link although its not working with MediaElement which i need :(
Related
im attempting to convert a canvas to a image source for use as an OpacityMask, I want to save it into memory rather than save it as a file, i'm having trouble though. Below is my code, I think i'm going about it wrong!
Really, I need to get the image information as a Base64String, so somewhere between that I need to convert the RenderTargetBitmap!
public BitmapSource ExportToPng(Uri path, Canvas surface)
{
BitmapEncoder encoder = new PngBitmapEncoder();
System.IO.MemoryStream myStream = new System.IO.MemoryStream();
// Save current canvas transform
Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
// Get the size of canvas
System.Windows.Size size = new System.Windows.Size(surface.ActualWidth, surface.ActualHeight);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(myStream);
// Restore previously saved layout
surface.LayoutTransform = transform;
var sr = new System.IO.StreamReader(myStream);
var myStr = sr.ReadToEnd();
var bytes = Convert.FromBase64String(myStr);
// Save to memory
/*Bitmap pg = new Bitmap("525, 350");
Graphics gr = Graphics.FromImage(pg);
gr.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(255, 255, 255, 255)), 0, 0, (float)size.Width, (float)size.Height);
gr.DrawImage(System.Drawing.Bitmap.FromStream(myStream), 0, 0);*/
return BitmapFromBase64(myStr);
}
public static BitmapSource BitmapFromBase64(string base64String)
{
var bytes = Convert.FromBase64String(base64String);
using (var stream = new System.IO.MemoryStream(bytes))
{
return BitmapFrame.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
Edit:
Just found another possible way, however this creates a DrawingVisual, I need to convert that to a ImageBrush
C#
// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle(List<Rectangle> rectangles)
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
foreach(Rectangle x in rectangles)
{
Rect rect = new Rect(new System.Windows.Point(x.X, x.Y), new System.Windows.Size(x.Width, x.Height));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.Black, (System.Windows.Media.Pen)null, rect);
}
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
A UIElement takes any Brush as OpacityMask. You can simply create a VisualBrush from you Canvas, since the base class of every UIElement is SWM.Visual.
Canvas c = new Canvas();
element.OpacityMask = new VisualBrush( c );
Regards, Snowball
So I am trying to take a snapshot of my canvas in WPF C# so that I can save it out as a png. The image saves incorrectly at present as it is including the left and top margins.
This is what I have:
create a rectangle for the size of the canvas. if canvas.Margin.Left and Top are set to 0 then the saved image is of the correct size but the offset still occurs and thus cuts the bottom and right edges. Being set the Margin.Left and Top still causes the offset to occur but the whole image is saved but at the wrong size (margin.Left + ActualWidth) rather than just ActualWidth
Rect rect = new Rect(canvas.Margin.Left, canvas.Margin.Top, canvas.ActualWidth, canvas.ActualHeight);
double dpi = 96d;
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, dpi, dpi, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes(filename, ms.ToArray());
}
catch (Exception err)
{
MessageBox.Show(err.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
Replace the first four lines with these lines
Rect bounds = VisualTreeHelper.GetDescendantBounds(canvas);
double dpi = 96d;
RenderTargetBitmap rtb = new RenderTargetBitmap((int)bounds.Width, (int)bounds.Height, dpi, dpi, System.Windows.Media.PixelFormats.Default);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(canvas);
dc.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
I have followed this article http://mcleodsean.wordpress.com/2008/10/07/bitmap-snapshots-of-wpf-visuals/ (for more explanation) and able to save the canvas without margins.
Im using DrawToBitmap to save some labels as image. I would like to know how to change the resolution of these images, is there a way? Say I have a label with a text and I want to render it to an image file (not posting the full code):
this.label1 = new System.Windows.Forms.Label();
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Baskerville Old Face", 36F);
//...
this.label1.Size = new System.Drawing.Size(161, 54);
this.label1.Text = "Output";
//...
//save image:
Bitmap image = new Bitmap(161, 54);
this.label1.DrawToBitmap(image, this.label1.ClientRectangle);
image.Save(#"C:\image.jpg");
This is working fine, I will get something like this:
The resolution is ok, but is it possible to increase it? When I zoom into this image a little, I can see the individual pixels as large blocks:
I know that's normal, because its not a vector graphic and that's fine. I just would like to change it somehow, so that you can zoom in further before seeing individual pixels as large blocks. Any ideas?
Thank you.
Edit: If Im using only black/white images - is it maybe better to save the image as png or gif?
Something like this will do the job, just increase font size used from label:
Bitmap CreateBitmapImage(string text, Font textFont, SolidBrush textBrush)
{
Bitmap bitmap = new Bitmap(1, 1);
Graphics graphics = Graphics.FromImage(bitmap);
int intWidth = (int)graphics.MeasureString(text, textFont).Width;
int intHeight = (int)graphics.MeasureString(text, textFont).Height;
bitmap = new Bitmap(bitmap, new Size(intWidth, intHeight));
graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.DrawString(text, textFont, textBrush,0,0);
graphics.Flush();
return (bitmap);
}
you can achieve this by increasing the value of second parameter of System.Drawing.Font.
this.label1.Font = new System.Drawing.Font("Baskerville Old Face", 1000F);
I am taking an image of a wpf control using this code:
BitmapEncoder imgEncoder = new PngBitmapEncoder();
RenderTargetBitmap bmpSource = new RenderTargetBitmap((int)element.ActualWidth, (int)element.ActualHeight, 150, 150, PixelFormats.Pbgra32);
bmpSource.Render(element);
imgEncoder.Frames.Add(BitmapFrame.Create(bmpSource));
using (MemoryStream ms = new MemoryStream())
{
imgEncoder.Save(ms);
bytes = ms.ToArray();
ms.Position = 0;
Image i = Image.FromStream(ms);
i.Save(#"C:\" + Guid.NewGuid().ToString() + "LARGE.png");
}
The trouble is ActualHeight/Width property gives the rendered height and width i.e. the displayed part. I want to save an image of the whole control even if some of the control is not visible on the screen i.e. it is placed in a scrollviewer.
How can I get the full size / height of a control so the bmpSource.Render() renders the whole control to an image?
private static void SaveUsingEncoder(string fileName, FrameworkElement UIElement, BitmapEncoder encoder)
{
int height = (int)UIElement.ActualHeight;
int width = (int)UIElement.ActualWidth;
// These two line of code make sure that you get completed visual bitmap.
// In case your Framework Element is inside the scroll viewer then some part which is not
// visible gets clip.
UIElement.Measure(new System.Windows.Size(width, height));
UIElement.Arrange(new Rect(new System.Windows.Point(), new Point(width, height)));
RenderTargetBitmap bitmap = new RenderTargetBitmap(width,
height,
96, // These decides the dpi factors
96,// The can be changed when we'll have preview options.
PixelFormats.Pbgra32);
bitmap.Render(UIElement);
SaveUsingBitmapTargetRenderer(fileName, bitmap, encoder);
}
private static void SaveUsingBitmapTargetRenderer(string fileName, RenderTargetBitmap renderTargetBitmap, BitmapEncoder bitmapEncoder)
{
BitmapFrame frame = BitmapFrame.Create(renderTargetBitmap);
bitmapEncoder.Frames.Add(frame);
// Save file .
using (var stream = File.Create(fileName))
{
bitmapEncoder.Save(stream);
}
}
Call this function as
SaveUsingEncoder(fileName, frameworkElement, new PngBitmapEncoder());
Hope this will help.
You could try temporarily taking the control out of its context (might cause problems if bound),transform it to a visivible point or scroll it into view for rendering.
Hi i'm creating an image in memory from a Canvas using a PngBitmapEncoder.
public void CaptureGraphic()
{
Canvas canvas = new Canvas();
canvas.SnapsToDevicePixels = true;
canvas.Height = IMAGEHEIGHT;
canvas.Width = IMAGEWIDTH;
Draw(canvas);
canvas.Arrange(new Rect(0, 0, IMAGEWIDTH, IMAGEHEIGHT));
member.MemberImage = GetPngFromUIElement(canvas);
}
public static System.Drawing.Image GetPngFromUIElement(Canvas source)
{
int width = (int)source.ActualWidth;
int height = (int)source.ActualHeight;
if (width == 0)
width = (int)source.Width;
if (height == 0)
height = (int)source.Height;
RenderTargetBitmap bitmap = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Pbgra32);
bitmap.Render(source);
PngBitmapEncoder enc = new PngBitmapEncoder();
enc.Interlace = PngInterlaceOption.Off;
enc.Frames.Add(BitmapFrame.Create(bitmap));
System.IO.MemoryStream ms = new System.IO.MemoryStream();
enc.Save(ms);
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
ms.Flush();
ms.Dispose();
return image;
}
Then i'm sending the image to the printer using the GDI+ DrawImage() method. However the printed result is blurry.
I've tried to match the original canvas size to the printed size to avoid any scaling, equally i've tried to make the original considerably bigger so the scaled image retains the quality however the final printed image is always blurred.
Can anyone offer any suggestions/alternatives. I have a considerable GDI+ print routine already setup and moving to wpf documents is not an option just yet.
Thanks
You're capturing the bitmap at 96 DPI. Instead of using 96 in the constructor of the RenderTargetBitmap, try to match the DPI of your printer output. Alternatively, you could do the math and calculate the difference in width/height and rescale the image on the report accordingly (the result is the image on the report will appear smaller).
I had the same blurry result and have come up with the following piece of code, which applies the same idea with the offset, but using the Offset property on the DrawingVisual (since I was using DrawDrawing, which doesn't have an overload with the offset argument):
public static Image ToBitmap(Image source)
{
var dv = new DrawingVisual();
// Blur workaround
dv.Offset = new Vector(0.5, 0.5);
using (var dc = dv.RenderOpen())
dc.DrawDrawing(((DrawingImage)source.Source).Drawing);
var bmp = new RenderTargetBitmap((int)source.Width, (int)source.Height, 96, 96, PixelFormats.Pbgra32);
bmp.Render(dv);
var bitMapImage = new Image();
bitMapImage.Source = bmp;
bitMapImage.Width = source.Width;
bitMapImage.Height = source.Height;
return bitMapImage;
}
Think i've found the answer.
http://www.charlespetzold.com/blog/2007/12/High-Resolution-Printing-of-WPF-3D-Visuals.html
I just needed to scale up the image size along with the dpi and voila, massively increased file size!
I had the same problem. To avoid blurry text and lines, I had to draw everything with an offset of 0.5 in X and Y direction. E.g, an horizontal line could be
drawingContext.DrawLine(pen, new Point(10.5,10.5), new Point(100.5,10.5));
In my case, I was rendering to a RenderTargetBitmap in a different thread to improve the UI performance. The rendered bitmap is then frozen and drawn onto the UI using
drawingContext.DrawImage(bitmap, new Rect(0.5, 0, bitmap.Width, bitmap.Height));
Here, I needed an additional offset of 0.5, but (strangely) only in X direction, so that the rendered image did not look Blurry any more.