I want to resize images for iOS so that I don't have to do it manually all the time. This should be done using WPF but it throws a ObjectDisposedException.
What it does is the user selects a csproj file and then an image which will be resized in 3 sizes. After resizing the file should be safed to disk but it throws an exception.
retrieving the bytes of the original BitmapImage
byte[] data = original.GetByteArray();
The used method:
public static byte[] GetByteArray(this BitmapImage bmi)
{
byte[] data;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmi));
using (Sys.MemoryStream ms = new Sys.MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
the resizing:
private BitmapImage ResizeImage(byte[] imageData, double dheight, double dwidth)
{
int height = (int)dheight;
int width = (int)dwidth;
BitmapImage resizedImage = new BitmapImage();
using (Sys.MemoryStream ms = new Sys.MemoryStream(imageData))
{
resizedImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
resizedImage.CacheOption = BitmapCacheOption.OnLoad;
resizedImage.DecodePixelHeight = height;
resizedImage.DecodePixelWidth = width;
resizedImage.BeginInit(); // Needed only so we can call EndInit()
resizedImage.StreamSource = ms;
resizedImage.EndInit();// This does the actual loading and resizing
}
return resizedImage;
}
then the saving of the files:
public static void Save(this BitmapImage image, string filePath)
{
//PngBitmapEncoder encoder = new PngBitmapEncoder();
//encoder.Frames.Add(BitmapFrame.Create(image));
//using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
//{
// encoder.Save(fileStream);
//}
File.WriteAllBytes(filePath, image.GetByteArray());
}
how I used it:
var bmi512 = ResizeImage(data, 512, 512);
var bmi256 = ResizeImage(data, 256, 256);
var bmi128 = ResizeImage(data, 128, 128);
bmi512.Save(Sys.Path.Combine(imageFolderPath, String.Format("{0}512{1}", imageName, imageExt)));
bmi256.Save(Sys.Path.Combine(imageFolderPath, String.Format("{0}256{1}", imageName, imageExt)));
bmi128.Save(Sys.Path.Combine(imageFolderPath, String.Format("{0}128{1}", imageName, imageExt)));
it works for retrieving the byte[] of the original but when it try it for bmi512 I get the exception.
Thanks in advance
You have to call BeginInit before you set any of the BitmapImage's properties:
using (var ms = new MemoryStream(imageData))
{
resizedImage.BeginInit();
resizedImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
resizedImage.CacheOption = BitmapCacheOption.OnLoad;
resizedImage.DecodePixelHeight = height;
resizedImage.DecodePixelWidth = width;
resizedImage.StreamSource = ms;
resizedImage.EndInit();
}
Related
I am using WPF objects to generate an bitmap image in memory. The program that does this resides in a WCF web service. The image renders correctly when I run on locally on IISExpress, and on a test IIS 7 server. However, when running on a server used by QA, the image is not rendered correctly. More specifically, only the top 22px lines of a 250px height image are rendered. The settings on both the test server and the QA server are supposed to be identical (insert skeptical face here).
Question: What possible settings in IIS could be effecting this image rendering? Also, I'm thinking there could possibly be a threading issue since RenderTargetBitmap renders asynchronously, and I do get a partial image.
Here is the code I'm using:
private byte[] RenderGauge(ViewData viewData)
{
double resolution = 4 * ReSize;
double dpi = 96 * resolution;
var view = new Gauge();
var vm = new GuageViewModel(viewData);
view.Measure(new Size(350, 70));
view.Arrange(new Rect(new Size(350, 70)));
var bounds = VisualTreeHelper.GetDescendantBounds(view);
if (bounds != Rect.Empty)
{
height = (int)(Math.Floor(bounds.Height) + 1);
width = (int)(Math.Floor(bounds.Width) + 1);
size = new Size(width, height);
}
var bitmap = new RenderTargetBitmap((int)(width * resolution), (int)(height * resolution), dpi, dpi, PixelFormats.Pbgra32);
var visual = new DrawingVisual();
using (var context = visual.RenderOpen())
{
var brush = new VisualBrush(view);
context.DrawRectangle(brush, null, new Rect(new Point(), bounds.Size));
}
bitmap.Render(visual);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
byte[] img;
using (var MS = new MemoryStream())
{
encoder.Save(MS);
img = MS.ToArray();
}
img = img == null ? new byte[0] : img;
return img;
}
So, I'm doing exactly the same thing and I had a number of issues rendering files. I've found that using a binding to a bitmap in the XAML helps. The code from my view model that returns the image source is:
public Uri ImageUri
{
get { return new Uri(ImagePath, UriKind.Absolute); }
}
public BitmapImage ImageSource
{
get
{
try
{
if (string.IsNullOrEmpty(ImagePath) || !File.Exists(ImagePath))
return null;
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = ImageUri;
image.EndInit();
return image;
}
catch (Exception e)
{
var logger = LogManager.GetLogger(typeof(ImageDetails));
ExceptionHelper.LogExceptionMessage(logger, e);
}
return null;
}
}
Then in the XAML I bind to the ImageSource property.
I think that most problems with RenderTargetBitmap are related to asynchronous bindings in the XAML becauses the render method is synchronous.
I new to WPF and I am trying to render simple line to bitmap and save it to the PNG file. But I got empty Bitmap instead.
What I am doing wrong?
void RenderLineToFile()
{
var bitmap = RenderBitMap();
SaveImageToFile("image.png", bitmap);
}
RenderTargetBitmap RenderBitMap()
{
int bitmapWidth = 100;
int bitmapHeight = 100;
double dpiX = 72;
double dpiY = 72;
RenderTargetBitmap bm = new RenderTargetBitmap(bitmapWidth, bitmapHeight, dpiX, dpiY, PixelFormats.Pbgra32);
DrawingVisual drawing_visual = new DrawingVisual();
using (DrawingContext drawing_context = drawing_visual.RenderOpen())
{
Pen penBlack = new Pen(Brushes.Black, 1.0);
drawing_context.DrawLine(penBlack, new Point(0, 0), new Point(100, 100));
bm.Render(drawing_visual);
}
return bm;
}
public static void SaveImageToFile(string filePath, BitmapSource image)
{
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(fileStream);
}
}
According to MSDN,
A DrawingContext must be closed before its content can be rendered...
Try taking bm.Render(drawing_visual); outside of the using clause.
Using WPF 4.5
private Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (var outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
}
and then later:
if (Clipboard.ContainsImage())
{
var bitmapSouce = Clipboard.GetImage();
var bitmap = BitmapFromSource(bitmapSouce);
var tmp = Path.GetTempFileName();
bitmap.Save(tmp, ImageFormat.Png);
...
bitmap.Save() throws an ExternalException, "A generic error in GDI+"
Is it really so hard to save a clipboard image to disk?
It is not necessary to create a System.Drawing.Bitmap (which is WinForms) from a WPF BitmapSource just for saving it.
You could as well directly save to a FileStream:
private void SaveBitmap(BitmapSource bitmapSource, string fileName)
{
var enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var outStream = new FileStream(fileName, FileMode.Create))
{
enc.Save(outStream);
}
}
...
var bitmapSource = Clipboard.GetImage();
var tmp = Path.GetTempFileName();
SaveBitmap(bitmapSource, tmp);
I am new to windows phone dev. My small app need a bytesarray from image (photo gallery). I tried many ways to convert, but it did not work fine.
here is my code:
public static byte[] ConvertBitmapImageToByteArray(BitmapImage bitmapImage)
{
using (var ms = new MemoryStream())
{
var btmMap = new WriteableBitmap(bitmapImage.PixelWidth, bitmapImage.PixelHeight);
// write an image into the stream
btmMap.SaveJpeg(ms, bitmapImage.PixelWidth, bitmapImage.PixelHeight, 0, 100);
return ms.ToArray();
}
}
But then I saved this byte array to image in photogallery, I was be a black image!
public static void SavePicture2Library(byte[] bytes)
{
var library = new MediaLibrary();
var name = "image_special";
library.SavePicture(name, bytes);
}
Could anyone help me?
Please test your code :( Thanks so much!
Update resolved!
var wBitmap = new WriteableBitmap(bitmapImage);
wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
stream.Seek(0, SeekOrigin.Begin);
data = stream.GetBuffer();
To anyone who finds this, this works;
Image to bytes;
public static byte[] ImageToBytes(BitmapImage img)
{
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap btmMap = new WriteableBitmap(img);
System.Windows.Media.Imaging.Extensions.SaveJpeg(btmMap, ms, img.PixelWidth, img.PixelHeight, 0, 100);
img = null;
return ms.ToArray();
}
}
Bytes to Image
public static BitmapImage BytesToImage(byte[] bytes)
{
BitmapImage bitmapImage = new BitmapImage();
try
{
using (MemoryStream ms = new MemoryStream(bytes))
{
bitmapImage.SetSource(ms);
return bitmapImage;
}
}
finally { bitmapImage = null; }
}
for windows phone 8
using System.IO;
public static class FileToByteArray
{
public static byte[] Convert(string pngBmpFileName)
{
System.IO.FileStream fileStream = File.OpenRead(pngBmpFileName);
using (MemoryStream memoryStream = new MemoryStream())
{
fileStream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
}
byte[] PasPhoto = FileToByteArray.Convert("Images/NicePhoto.png")
I need to save the content of a WPF Object as an Image file. In my application I have a chart drawn on a Canvas object. This is what I need to save. The Canvas with all child objects.
What you're looking for is the RenderTargetBitmap class. There's an example of its use on the MSDN page I linked, and there's another good example that includes saving to a file here:
RenderTargetBitmap by Eric Sinc
Here is the func which creates RenderTargetBitmap object, that will be used in further funcs.
public static RenderTargetBitmap ConvertToBitmap(UIElement uiElement, double resolution)
{
var scale = resolution / 96d;
uiElement.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
var sz = uiElement.DesiredSize;
var rect = new Rect(sz);
uiElement.Arrange(rect);
var bmp = new RenderTargetBitmap((int)(scale * (rect.Width)), (int)(scale * (rect.Height)), scale * 96, scale * 96, PixelFormats.Default);
bmp.Render(uiElement);
return bmp;
}
This functionc creates JPEG string content of file and writes it to a file:
public static void ConvertToJpeg(UIElement uiElement, string path, double resolution)
{
var jpegString = CreateJpeg(ConvertToBitmap(uiElement, resolution));
if (path != null)
{
try
{
using (var fileStream = File.Create(path))
{
using (var streamWriter = new StreamWriter(fileStream, Encoding.Default))
{
streamWriter.Write(jpegString);
streamWriter.Close();
}
fileStream.Close();
}
}
catch (Exception ex)
{
//TODO: handle exception here
}
}
}
This function used above to create JPEG string representation of Image content:
public static string CreateJpeg(RenderTargetBitmap bitmap)
{
var jpeg = new JpegBitmapEncoder();
jpeg.Frames.Add(BitmapFrame.Create(bitmap));
string result;
using (var memoryStream = new MemoryStream())
{
jpeg.Save(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
using (var streamReader = new StreamReader(memoryStream, Encoding.Default))
{
result = streamReader.ReadToEnd();
streamReader.Close();
}
memoryStream.Close();
}
return result;
}
Hope this helps.
With the help of the Eric Sinc tutorial I came to the following solution:
It uses a win32 SaveDialog to choose where the file should go and a PngBitmapEncoder (many other BitmapEncoders available!) to convert it to something we can save.
Note that the element being saved in this example is "cnvClasses" and that the size of the output is, quite deliberately, the same as the control.
SaveFileDialog svDlg = new SaveFileDialog();
svDlg.Filter = "PNG files|*.png|All Files|*.*";
svDlg.Title = "Save diagram as PNG";
if (svDlg.ShowDialog().Value == true)
{
RenderTargetBitmap render = new RenderTargetBitmap((int)this.cnvClasses.ActualWidth, (int)this.cnvClasses.ActualHeight, 96, 96, PixelFormats.Pbgra32);
render.Render(cnvClasses);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(render));
using (FileStream fs = new FileStream(svDlg.FileName, FileMode.Create))
encoder.Save(fs);
}