transform a BitmapImage and Save it - c#

I'm doing an image viewer (in a screen saver so images change randomly) in WPF and i would like to rotate a picture and save it at the same time (so it's rotated and i dont need to bother anymore).
I'm using Doubleanimation to fade one picture to the next.
Here is the function to rotate the image :
private void RotateImage(string path, float rotationAngle)
{
BitmapImage CurrentImage = new BitmapImage();
TransformedBitmap newTransformedImage = new TransformedBitmap();
Image img = new Image();
CurrentImage.BeginInit();
CurrentImage.UriSource = new Uri(path);
CurrentImage.EndInit();
newTransformedImage.BeginInit();
newTransformedImage.Source = CurrentImage;
RotateTransform transform = new RotateTransform(90);
newTransformedImage.Transform = transform;
newTransformedImage.EndInit();
img.Source = newTransformedImage;
Savetojpg(img, path);
}
and here is the funtion to save:
private void Savetojpg(Image image, string filePath)
{
var encoder = new JpegBitmapEncoder();
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)image.ActualWidth, (int)image.ActualHeight, 96, 96, PixelFormats.Pbgra32);
Size visualSize = new Size(image.ActualWidth, image.ActualHeight);
image.Measure(visualSize);
image.Arrange(new Rect(visualSize));
bitmap.Render(image);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.QualityLevel = 100;
encoder.Frames.Add(frame);
using (var stream = File.Create(filePath))
{
encoder.Save(stream);
}
}
When i run this, i have a System.ArgumentOutOfRangeException at the first line of the Savetojpg function as it seems the image i'm sending to that function is empty.
Can anyone tell me what i am doing wrong ?

Related

Show a rendered image of a UIelement(Visual) in an Image control WPF

I'm developing an application and I would like to implement a feature similar to Microsoft Power Point's paging system where there is a smaller image of the sheets you are working on on the side.
I have the following function which renders an image of the Grid in which the user has to work.
private void CreateImageOfPage()
{
int width = (int)this.WorkSheetViewModelList.Last().RootGrid.Width;
int height = (int)this.WorkSheetViewModelList.Last().RootGrid.Height;
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(this.WorkSheetViewModelList.Last().RootGrid);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
Guid photoID = System.Guid.NewGuid();
String photolocation = "Temp/"+photoID.ToString() + ".png";
using (var filestream = new FileStream(photolocation, FileMode.Create))
encoder.Save(filestream);
Console.WriteLine(Path.GetFullPath(photoID.ToString() + ".png"));
}
My question is, how to get, set or format the path of the saved image to be set as Source of an Image in a XAML? In its current form the path looks like this : "D:\Project\bin\Debug\7fd77420-f62e-44e7-b206-b5ce19f01a9e.png", and it says it could not get the file at this location(for now I just copied the output in, but it will be bound, as it should be).
If the path I've taken is not elegant, is there any other way to do it better, or without saving the image to the disk at all?
Thanks!
You don't need to do any image encoding.
Assuming that there is an Image element named image, you could directly assign the RenderTargetBitmap to its Source property like this:
image.Source = renderTargetBitmap;
You may change your method to this:
private ImageSource CreateImageOfPage()
{
var rootGrid = WorkSheetViewModelList.Last().RootGrid;
var width = (int)rootGrid.ActualWidth;
var height = (int)rootGrid.ActualHeight;
var renderTargetBitmap = new RenderTargetBitmap(
width, height, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(rootGrid);
return renderTargetBitmap;
}
and use it like this:
image.Source = CreateImageOfPage();

Image gets mangled when saving InkCanvas to byte array to file

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();
}
}

save Image from Image-Control wpf c#

I made a QR-Code Encoder (WPF, c#) by using ZXing.net
I am displaying the QR-Code in an Image-Control
var writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new ZXing.Common.EncodingOptions
{
Height = 200,
Width = 200,
Margin = 0
}
};
var image = writer.Write(qrtext.Text);
qrImg.Source = image;
After that I want to save the image. I was using this example Save Image in a Folder.
private void btnSaveImg_Click(object sender, RoutedEventArgs e)
{
string filePath = #"C:\Users\xxx\Desktop\image.png";
SaveToPng(qrImg, filePath);
}
void SaveToBmp(FrameworkElement visual, string fileName)
{
var encoder = new BmpBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
void SaveToPng(FrameworkElement visual, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
// and so on for other encoders (if you want)
void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.ActualWidth, (int)visual.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
Unfortunately the image is not being saved. Furthermore, I get no exception. Hope you see my mistake.
Thx a lot
I found another solution, based on this post: How can I save the picture on image control in wpf?
So my solution, that works for me, is:
String filePath = #"C:\Users\xxx\Desktop\test.jpg";
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)qrImg.Source));
using (FileStream stream = new FileStream(filePath, FileMode.Create))
encoder.Save(stream);

Saving Images from Image Control isn't working

I have a Image (Frameworkelement) on my GUI.
There is a image in there. Now I'm performing a doubleclick at this image and I want, that
the Image saves itself and is going to be opened, with the default imageviewer.
My Code:
void image_MouseDown(object sender, MouseButtonEventArgs e)
{
//Wayaround, cause there is no DoubleClick Event on Image
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
{
SaveToPng(((Image)sender), "SavedPicture.png");
Process.Start("SavedPicture.png");
}
}
void SaveToPng(FrameworkElement visual, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(visual, fileName, encoder);
}
void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
{
RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)visual.ActualWidth,
(int)visual.ActualHeight,
96,
96,
PixelFormats.Pbgra32);
bitmap.Render(visual);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
Opening the picture works fine with Process.Start. The problem is the saving, well it saves the picture: SavedPicture.png but, Its just black, so theres no graphic.. Maybe someone could tell me, whats wrong in my code or knows a better way of saving a image in WPF.
It is necessary that the image is displayed before it is saved. So, if you want to use RenderTargetBitmap just set the Image.Source and load the Image before saving with SaveToPng (ActualWidth and ActualHeight must not be null).
Example:
If you have the Image inside a Panel:
<Grid x:Name="MyGrid">
<Image x:Name="MyImage"/>
</Grid>
I set Image.Source in my test class constructor, and only after the image was loaded i save it:
public MainWindow()
{
InitializeComponent();
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.UriSource = new Uri("image.png", UriKind.RelativeOrAbsolute);
bmp.EndInit();
MyImage.Source = bmp;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
RenderTargetBitmap bmp = new RenderTargetBitmap((int)MyGrid.ActualWidth,
(int)MyGrid.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(MyImage);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (var stream = System.IO.File.Create("newimage.png"))
{ encoder.Save(stream); }
}
If you don't want to use Grid ActualWidth and ActualHeight just pass your with and height as arguments.
Depends on the type of the Image.Source, assuming that you have a BitmapSource as in the article it should be along those lines:
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)image.Source));
using (FileStream stream = new FileStream(filePath, FileMode.Create))
encoder.Save(stream);
by the way RenderTargetBitmap class is to Converts a Visual object into a bitmap. its recommended by the team
sample
http://msdn.microsoft.com/en-us/library/aa969819.aspx
Problem solved.
I used this: File.WriteAllBytes()
to save the image from binaryformat

Rendering an image at runtime in WPF

I have posted several questions related to this problem I am having and I am starting to believe this cannot be done. Here is the back story.
I have an ASP.NET application from which I want to generate a .png image. This .png image needs to be constructed from either XAML or a WPF Visual Tree. Because of this, I must generate the .png image in an STA thread. Everything works fine until my XAML/WPF Visual Tree includes an Image (as in a System.Windows.Controls.Image). My .png file gets generated correctly except the Image element does not show the referenced picture. The referenced picture is located at a remote URL. No errors or exceptions are thrown.
How do I create a .png image from some XAML/WPF Visual Tree that includes a System.Windows.Controls.Image element? The resulting .png must include the picture referenced in the Image element. I have tried the following code in a variety of ways:
string address = "http://imgtops.sourceforge.net/bakeoff/o-png24.png";
WebClient webClient = new WebClient();
byte[] imageContent = webClient.DownloadData(address);
Image image = new Image();
using (MemoryStream memoryStream = new MemoryStream(imageContent))
{
BitmapImage imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.StreamSource = memoryStream;
imageSource.EndInit();
image.Source = imageSource;
}
// Set the size
image.Height = 200;
image.Width = 300;
// Position the Image within a Canvas
image.SetValue(Canvas.TopProperty, 1.0);
image.SetValue(Canvas.LeftProperty, 1.0);
Canvas canvas = new Canvas();
canvas.Height = 200;
canvas.Width = 300;
canvas.Background = new SolidColorBrush(Colors.Purple);
canvas.Children.Add(image);
// Create the area
Size availableSize = new Size(300, 200);
frameworkElement.Measure(availableSize);
frameworkElement.Arrange(new Rect(availableSize));
// Convert the WPF representation to a PNG file
BitmapSource bitmap = RenderToBitmap(frameworkElement);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
// Generate the .png
FileStream fileStream = new FileStream(filename, FileMode.Create);
encoder.Save(fileStream);
public BitmapSource RenderToBitmap(FrameworkElement target)
{
int actualWidth = 300;
int actualHeight = 200;
Rect boundary = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(actualWidth, actualHeight, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext context = drawingVisual.RenderOpen())
{
VisualBrush visualBrush = new VisualBrush(target);
context.DrawRectangle(visualBrush, null, new Rect(new Point(), boundary.Size));
}
renderBitmap.Render(drawingVisual);
return renderBitmap;
}
Thank you for your help.
You are rendering the output bitmap correctly, it is just the input bitmap you are screwwing up :).
BitmapImage requires access to the StreamSource property until it fires the DownloadCompleted event, but the 'using' block Dispose()s of the MemoryStream before it has a chance! You could simply unwrap the MemoryStream from the using block and let the GC handle it (if you do, I would recommend setting the BitmapImage.CacheOption to BitmapCacheOption.None, so it uses the stream directly, rather than a copy), but I would use the UriSource property and wait for the DownloadComplete event before rendering.

Categories

Resources