I have a UniformGrid containing all my video thumbnails taken (they are all System.Windows.Control.Image). My goal here is to save a jpg of all the thumbnails after I click a button. Is there a way to grab a bitmap image or something from a UbiformGrid?
I am using C# with WPF.
Edit: like a screenshot. But I don't want to window border, only the grid content.
Edit2: I finally found a solution. Thanks for the help.
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)ThumbnailPanel.Width,
(int)ThumbnailPanel.Height, 96, 96, PixelFormats.Pbgra32);
VisualBrush sourceBrush = new VisualBrush(ThumbnailPanel);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(ThumbnailPanel.Width, ThumbnailPanel.Height)));
}
renderTarget.Render(drawingVisual);
JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.QualityLevel = 80;
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget));
Byte[] _imageArray;
using (MemoryStream outputStream = new MemoryStream())
{
jpgEncoder.Save(outputStream);
_imageArray = outputStream.ToArray();
}
FileStream fileStream = new FileStream(#"myThumbnails.jpg", FileMode.Create, FileAccess.ReadWrite);
BinaryWriter binaryWriter = new BinaryWriter(fileStream);
binaryWriter.Write(_imageArray);
binaryWriter.Close();
Sure, just iterate through your collection of images with for or foreach and use the Image.GetThumbnailImage method from the System.Drawing namespace to create thumbnails... it's that simple.
For example:
foreach (var img in myImages)
{
var thumb = image.GetThumbnailImage(thumbnailSize.Width, thumbnailSize.Height, null, IntPtr.Zero);
//Do something with the thumbnail
thumb.Save(output)
}
Related
with the code I gave, I can take a screenshot of the section where the canvas is located. However, I am able to get on the predetermined path. What I want to do is I want to save the image to the section I want via savefiledialog. How can I do that.
private void btnKaydet_Click(object sender, RoutedEventArgs e)
{
UIElement element = cnvs as UIElement;
Uri path = new Uri(#"c:\screenshot.png");
CaptureScreen(element, path);
}
public void CaptureScreen(UIElement source, Uri destination)
{
try
{
double Height, renderHeight, Width, renderWidth;
Height = renderHeight = source.RenderSize.Height;
Width = renderWidth = source.RenderSize.Width;
//Specification for target bitmap like width/height pixel etc.
RenderTargetBitmap renderTarget = new
RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96,
PixelFormats.Pbgra32);
//creates Visual Brush of UIElement
VisualBrush visualBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext =
drawingVisual.RenderOpen())
{
//draws image of element
drawingContext.DrawRectangle(visualBrush, null, new
Rect(new System.Windows.Point(0, 0), new System.Windows.Point(Width, Height)));
}
//renders image
renderTarget.Render(drawingVisual);
//PNG encoder for creating PNG file
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new
FileStream(destination.LocalPath, FileMode.Create,
FileAccess.Write))
{
encoder.Save(stream);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
We can use the FileSaveDialog class.
In your using statement place
using Microsoft.Win32;
using System.Windows;
You may have to add a reference to System.Windows.Forms
A popular WPF open source dialog library can be found here as well if you opt not use the Microsoft version.
https://github.com/ookii-dialogs/ookii-dialogs-wpf
private void BtnKaydet_OnClick(object sender, RoutedEventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
if (saveFileDialog.ShowDialog() == true)
{
UIElement element = this.cnvs as UIElement;
Uri path = new Uri(saveFileDialog.FileName);
CaptureScreen(element, path);
}
}
public void CaptureScreen(UIElement source, Uri destination)
{
try
{
double Height, renderHeight, Width, renderWidth;
Height = renderHeight = source.RenderSize.Height;
Width = renderWidth = source.RenderSize.Width;
//Specification for target bitmap like width/height pixel etc.
RenderTargetBitmap renderTarget =
new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32);
//creates Visual Brush of UIElement
VisualBrush visualBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
//draws image of element
drawingContext.DrawRectangle(visualBrush, null,
new Rect(new System.Windows.Point(0, 0), new System.Windows.Point(Width, Height)));
}
//renders image
renderTarget.Render(drawingVisual);
//PNG encoder for creating PNG file
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (FileStream stream = new FileStream(destination.LocalPath, FileMode.Create, FileAccess.Write))
{
encoder.Save(stream);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
You can also specify allowed file extension types(.png,.jpg etc.) with the filter parameters.
https://wpf-tutorial.com/dialogs/the-savefiledialog/
I am saving the rendered image using render target bitmap, and it is saved properly in the given size, but when I set background to the grid in which image is placed, I am getting different output. Can any one explain this behavior?
<Grid x:Name="grid1" Grid.Row="0" Background="Red">
<Image x:Name="image1" Source="Images/butterfly.jpg" >
</Image>
</Grid>
Code behind
RenderTargetBitmap result = GetImage(this.grid1);
Stream imageStream = new MemoryStream();
SaveAsPng(result, imageStream);
public static RenderTargetBitmap GetImage(Grid view)
{
Size size = new Size(1122, 750);
if (size.IsEmpty)
return null;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(view), null, new Rect(new Point(), size));
context.Close();
}
result.Render(drawingvisual);
return result;
}
public static void SaveAsPng(RenderTargetBitmap src, Stream outputStream)
{
var saveFileDialog = new SaveFileDialog()
{
Filter = "Image Files (*.bmp, *.png, *.jpg)|*.bmp;*.png;*.jpg"
};
if (saveFileDialog.ShowDialog() == true)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
using (FileStream stream = new FileStream(saveFileDialog.FileName, FileMode.Create))
encoder.Save(stream);
}
}
Without Background
With Background
In order to retain the original element dimensions in the DrawingVisual, you should set the VisualBrush's Stretch to None. If necessary, you can also get precise control of the placement of the visual by setting the VisualBrush's Viewport, Viewbox, AlignmentX and AlignmentY properties.
Also consider passing the result size as an argument to your GetImage method, and use the most general type for the view argument:
public static BitmapSource GetImage(Visual view, Size size)
{
var bitmap = new RenderTargetBitmap(
(int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
var visualBrush = new VisualBrush
{
Visual = view,
Stretch = Stretch.None
};
var drawingvisual = new DrawingVisual();
using (var context = drawingvisual.RenderOpen())
{
context.DrawRectangle(visualBrush, null, new Rect(size));
}
bitmap.Render(drawingvisual);
return bitmap;
}
Also make the SaveAsPng method more flexible by changing the argument type. The outputStream argument isn't used at all, so remove it.
public static void SaveAsPng(BitmapSource src)
Then call both methods like this:
var result = GetImage(grid1, new Size(1122, 750));
SaveAsPng(result);
I have a WPF application using InkCanvas. When I render the bitmap, save to a memory stream, write the resulting bytes to a file, and then open that file in paint, the image is mangled. Any idea what I may be doing wrong here? Tried several solutions found here on SO and also on codeproject. It's pretty clear that it's capturing part of the InkCanvas but the majority of it is black (I assume null bytes).
EDIT: also tried with/without margin. Here are the other links I've tried:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ef71237c-5dfb-4d6c-a402-e8cb02b74e99/how-to-convert-inkcanvas-strokes-to-a-bitmap-or-byte-array?forum=wpf
Converting InkCanvas Strokes to a Byte Array and back again
InkCanvas Load/Save operations
http://www.centrolutions.com/Blog/post/2008/12/09/Convert-WPF-InkCanvas-to-Bitmap.aspx
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ba4dc89f-0169-43a9-8374-68e1fb34a222/saving-inkcanvas-as-image?forum=wpf
I need the resultant file to be a bitmap/PNG so it can be viewed on another machine.
private byte[] ConvertInkCanvasToByteArray()
{
int margin = (int)this.icSignature.Margin.Left;
int width = (int)this.icSignature.ActualWidth - margin;
int height = (int)this.icSignature.ActualHeight - margin;
RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Default);
rtb.Render(icSignature);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
byte[] bitmapBytes;
using (MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
ms.Position = 0;
bitmapBytes = ms.ToArray();
}
return bitmapBytes;
}
From the InkCanvas:
And then mangled:
To avoid any problem with the InkCanvas' Margin, you could draw it into an intermediate DrawingVisual:
private byte[] ConvertInkCanvasToByteArray()
{
var rect = new Rect(icSignature.RenderSize);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new VisualBrush(icSignature), null, rect);
}
var rtb = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height, 96d, 96d, PixelFormats.Default);
rtb.Render(visual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}
My goal is to load .png images with transparency via URI, crop them and draw them on a canvas, then save the canvas images as a png file.
In javascript, it would look like:
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = "a.png";
ctx.drawImage(img,10,10,20,20,30,30,10,10);
//drawing more images...
something(canvas.toDataURL('image/png'));
How could I do that in C# Visual Studios 2013? What would come the closest to this JS code?
I don't mind using WPF or Winforms. I do not need to be able to display the image. I only need to be able to save it.
One way to do it is with GDI+ (assumes using System.Drawing;):
using (var b = new Bitmap()) { // This is your canvas
Graphics g = Graphics.FromImage(b); // This is your graphics context
g.DrawImage(Image.FromFile("a.png"),
new Rectangle(30, 30, 10, 10), 10, 10, 20, 20,
GraphicsUnit.Pixel);
// Do something with b (e.g. b.Save(…))
}
If you want the same data URI, it’s (assumes using System.Drawing.Imaging;):
using (var ms = new MemoryStream()) {
using (var b = new Bitmap()) {
// …
b.Save(ms, ImageFormat.Png);
}
string base64 = Convert.ToBase64String(ms.ToArray());
something("data:image/png;base64," + base64);
}
You can use WPF as an image generator.
The namespaces you'll need include:
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
And a sample snippet of code to get you started:
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Let's draw a rectangle!
var gradientBrush = new LinearGradientBrush();
gradientBrush.GradientStops.Add(new GradientStop(Colors.Azure, 0.0));
gradientBrush.GradientStops.Add(new GradientStop(Colors.SteelBlue, 1.0));
drawingContext.DrawRectangle(gradientBrush, null, new Rect(0, 0, _imageWidth, _imageHeight));
drawingContext.Close();
// Now to save it
RenderTargetBitmap bmp = new RenderTargetBitmap(_imageWidth, _imageHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(bmp));
byte[] imageBinary = null;
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
png.Save(memoryStream);
imageBinary = memoryStream.GetBuffer();
}
I want to save my canvas to image. It works but background color is black. How I must add to change the color?
I use this code:
Size size = new Size(surface.Width, surface.Height);
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);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(filename, FileMode.Create))
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
Try this
Size size = new Size(surface.Width, surface.Height);
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);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
VisualBrush visualBrush = new VisualBrush(surface);
drawingContext.DrawRectangle(visualBrush, null,
new Rect(new Point(), new Size(size.Width, size.Height)));
}
renderBitmap.Render(drawingVisual);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(filename, FileMode.Create))
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
Try PixelFormats.Default or PixelFormats.Bgra32 or PixelFormats.Rgb24 instead of PixelFormats.Pbgra32.
The P stands for pre-multiplied - the assumption is that each channel is pre-multiplied by alpha.
MSDN reference