I'm trying to "copy" an image from another image, reduce it's height and width, and return it as stream to retrieve in another class and show as an Image. But when I get the stream in the other class, and exception is thrown; "cannot access to closed stream".
This method gets the image path, reduces its size and returns as a stream.
public Stream getImagenCopia (string dataImagen)
{
Bitmap ImageOrig = BitmapFactory.DecodeFile (dataImagen);
var ImagenCopia = Bitmap.CreateScaledBitmap (ImageOrig, 80, 80, false);
using (MemoryStream ms = new MemoryStream ())
{
ImagenCopia.Compress (Bitmap.CompressFormat.Jpeg, 40, ms);
return ms;
}
}
This method receives the stream and sets it in Image source
var cim = auxFotos.getImagenCopia(path);
setImagen(img, cim);
void setImagen (Image img, Stream strm)
{
img.Source = ImageSource.FromStream (() =>
{
return strm;
});
}
You're disposing of the MemoryStream because you're using a using block. By the time you try to use the return value later in your code, it's unavailable.
using (MemoryStream ms = new MemoryStream ())
{
ImagenCopia.Compress (Bitmap.CompressFormat.Jpeg, 40, ms);
return ms;
}
Remove the using statement:
Bitmap ImageOrig = BitmapFactory.DecodeFile(dataImagen);
var ImagenCopia = Bitmap.CreateScaledBitmap(ImageOrig, 80, 80, false);
var ms = new MemoryStream());
ImagenCopia.Compress(Bitmap.CompressFormat.Jpeg, 40, ms);
return ms;
If you see memory consumption spike because resources aren't being garbage collected, you may have to clean it up manually after you're done using it, perhaps by calling .Dispose() on it.
Related
For an application I'm currently developing, I need to create a .emf file.
I've got it working when I output the result directly to a file, but I can't get it to output into a stream, which is essential to what I'm trying to do.
This code correctly generates the file, but it's outputted directly to the harddisk.
var sizedImage = new Bitmap(103, 67);
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile("result.emf", graphicsFromSizedImage.GetHdc()))
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
}
Here's my attempt to output it to a memorystream, so I could get a byte[] from that stream:
byte[] resultingBytes;
var sizedImage = new Bitmap(103, 67);
using(var stream = new MemoryStream())
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile(stream, graphicsFromSizedImage.GetHdc()))
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
resultingBytes = stream.GetBuffer();
}
File.WriteAllBytes("result.emf", resultingBytes);
But all this does is create an empty file. When I run through it with the debugger, I can see the stream remain empty.
What am I missing here..?
I found the answer thanks to #Selvin
It turns out, the changes are only written to the MemoryStream when the "graphics" object is disposed. So, just by adding an extra set of brances, the problem is resolved.
Here's my working code:
byte[] resultingBytes;
var sizedImage = new Bitmap(103, 67);
using(var stream = new MemoryStream())
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile(stream, graphicsFromSizedImage.GetHdc()))
{
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
}
resultingBytes = stream.ToArray();
}
File.WriteAllBytes("result.emf", resultingBytes);
Edit: As some have pointed out, stream.GetBuffer() will return the entire buffer. I changed it to stream.ToArray(), which should be better.
I have saved images in our database by using the following method to convert them to byte arrays in different ImageFormats:
public byte[] foo()
{
Image img = Image.FromFile(path);
var tmpStream = new MemoryStream();
ImageFormat format = img.RawFormat;
img.Save(tmpStream, format);
tmpStream.Seek(0, SeekOrigin.Begin);
var imgBytes = new byte[MAX_IMG_SIZE];
tmpStream.Read(imgBytes, 0, MAX_IMG_SIZE);
return imgBytes;
}
Now I need to read them out and convert them back into the BitmapImage type so I can display them to the user. I was thinking about using the Image.FromStream(Stream) method but that doesn't seem to take into account the different ImageFormats... Anyone know what to do? Thanks in advance.
You shouldn't use classes from the WinForms System.Drawing namespace in a WPF application (like you do with Image.FromFile).
WPF provides its own set of classes to load and save bitmaps from Streams and URIs, and has built-in support for automatically detecting the format of a bitmap frame buffer.
Just create a BitmapImage or a BitmapFrame directly from a Stream:
public static BitmapSource BitmaSourceFromByteArray(byte[] buffer)
{
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(buffer))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze(); // optionally make it cross-thread accessible
return bitmap;
}
or
public static BitmapSource BitmaSourceFromByteArray(byte[] buffer)
{
using (var stream = new MemoryStream(buffer))
{
return BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
Either method returns a BitmapSource, which is the base class of BitmapImage and BitmapFrame, and should be sufficient to deal with bitmaps in the rest of your application. E.g. the Source property of an Image control uses another base class, ImageSource, as property type.
Note also that when you load a BitmapSource from a Stream that is to be closed after loading, you have to set BitmapCacheOption.OnLoad. Otherwise the Stream must be kept open until the bitmap is eventually shown.
For encoding a BitmapSource you should be using a method like this:
public static byte[] BitmapSourceToByteArray(BitmapSource bitmap)
{
var encoder = new PngBitmapEncoder(); // or any other BitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}
I have a code in form1 constructor:
ConvertedBmp = ConvertTo24(newest.FullName);
The function ConvertTo24 is:
private static Bitmap ConvertTo24(string inputFileName)
{
sw = Stopwatch.StartNew();
Bitmap bmpIn = (Bitmap)Bitmap.FromFile(inputFileName);
Bitmap converted = new Bitmap(bmpIn.Width, bmpIn.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(converted))
{
g.PageUnit = GraphicsUnit.Pixel;
g.DrawImageUnscaled(bmpIn, 0, 0);
}
sw.Stop();
return converted;
}
The problem is how can i use the ConvertedBmp in this line:
backTexture = TextureLoader.FromFile(D3Ddev, #"D:\test.bmp");
TextureLoader have some properties and two of them are: Fromfile and it's getting device and string or FromStream and it's getting device and Stream.
I have the device object already but how can i use the ConvertedBmp(Bitmap type) with the TextureLoader ?
Bitmap class has a method called Save() which accepts a Stream (for example a MemoryStream object) and an ImageFormat, use that. After saved the Bitmap into a MemoryStream you can use that with TextureLoader.
Image.Save Method (Stream, ImageFormat)
I get below code from here:
http://www.java2s.com/example/csharp/system.drawing/bitmap-to-memory-stream.html
public static MemoryStream ToMemoryStream(this Bitmap b)
{
MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms;
}
Work for my need
I'm inserting a TIFF file into a PDF using PDFSharp. That process works fine, but it's leaving a lock on the TIFF file. The TIFF file is on a SMB share. I am using the WPF version because the GDI version does not support CMYK TIFFs.
var output = new PdfDocument();
var input = PdfReader.Open(template_path, PdfDocumentOpenMode.Import);
var page = input.Pages[0];
output.AddPage(page);
page = output.Pages[0];
var gfx = XGraphics.FromPdfPage(page);
var image = XImage.FromFile(tiff_path);
gfx.DrawImage(image, 500, 200, 400, 400);
output.Save(destination_path);
output.Close();
Update: Simply doing this leaves the TIFF locked. No document opened or XGraphics or anything.
using (var image = XImage.FromFile(path))
{}
Update: This works, and is what I am going with for now.
using (var fsImage = File.Open(tiffPath, FileMode.Open, FileAccess.Read, FileShare.None))
{
var bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.StreamSource = fsImage;
bitmapSource.EndInit();
using (var image = XImage.FromBitmapSource(bitmapSource))
{
}
}
Indecently, this nasty piece of code works also :-)
using (var image = XImage.FromFile(tiffPath))
{
}
GC.Collect();
With WPF BitmapSource, there is no deterministic disposal of the underlying stream, so you can end up with locks for as long as there is a reference.
You --> XImage --> BitmapSource --> Stream
If you call dispose on the XImage, it will release its reference on the BitmapSource, which will allow it to be finalized when the GC feels like it.
You can control when the file is closed by providing stream in lieu of a path and closing it explicitly. Doing so prematurely will cause exceptions in BitmapSource, however, so be sure you are not using the BitmapSource after you close the stream.
using (var fsImage = File.Open(tiff_path, FileMode.Open, FileAccess.Read, FileShare.None))
{
var output = new PdfDocument();
var input = PdfReader.Open(template_path, PdfDocumentOpenMode.Import);
var page = input.Pages[0];
output.AddPage(page);
page = output.Pages[0];
var gfx = XGraphics.FromPdfPage(page);
var bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.StreamSource = fsImage;
bitmapSource.EndInit();
using (var image = XImage.FromBitmapSource(bitmapSource))
{
gfx.DrawImage(image, 500, 200, 400, 400);
}
output.Save(destination_path);
output.Close();
}
If your image is small enough, you could skip the stream and just use the BitmapCacheOption of OnLoad to close the source after opening, but this will cause the entire image to be loaded into memory.
I am trying to add some text on top of a photo take by the camera, and here is the method I am using, but unfortunately I either get a closedStream error, or that there is cross-thread access when I try use the dispatcher. Could someone please explain me what is going wrong?
void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
DateTime dt = DateTime.Now;
string fileName = dt.Year.ToString() + dt.Month.ToString() + dt.Day.ToString() + dt.Hour.ToString() + dt.Minute.ToString() + dt.Second.ToString() + ".jpg";
try
{
// Save picture to the library camera roll.
library.SavePictureToCameraRoll(fileName, e.ImageStream);
// Set the position of the stream back to start
e.ImageStream.Seek(0, SeekOrigin.Begin);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// load photo to writable bitmap
WriteableBitmap writeableBitmap = PictureDecoder.DecodeJpeg(e.ImageStream);
writeableBitmap.Invalidate();
var renderText = new TextBlock
{
Text = "Hello World",
FontSize = 72,
Foreground = new SolidColorBrush(Colors.White),
FontWeight = FontWeights.Black,
Width = 500,
Height = 100
};
writeableBitmap.Render(renderText, new TranslateTransform() { X = 100, Y = 300 });
writeableBitmap.Invalidate();
using (var ms = new MemoryStream())
{
writeableBitmap.SaveJpeg(ms, 1024, 768, 0, 100);
ms.Seek(0, SeekOrigin.Begin);
library.SavePicture("x" + fileName, ms);
}
// e.ImageStream.Close();
});
// Save picture as JPEG to isolated storage.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
{
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the image to isolated storage.
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}
With the code above I get the following error: Cannot access a closed Stream.
If i remove the Dispatcher, I get this error: Invalid cross-thread access.
Thanks.
place ui less code outside dispatcher scope.
or save your e.ImageStream to other stream can be gotted in dispatcher scope.
First, what is occurring?
Dispatcher.BeginInvoke postpones your code and tells the UI thread to execute it whenever it's available. Therefore, your e.ImageStream.Close(); line is executed before the code inside of the BeginInvoke. So when you're trying to read the contents of the stream, it's already closed.
Two ways to solve that:
Remove the e.ImageStream.Close(); from the finally block. But I don't know if the stream will remain open anyway.
If 1. doesn't work, copy the contents of the stream to a MemoryStream, then use this stream to create the WriteableBitmap:
var stream = new MemoryStream();
e.ImageStream.CopyTo(stream);
In both case, don't forget to close the stream when you're done creating the WriteableBitmap.