Why won't GDI let me delete large images? - c#

My ASP.NET application has an image cropping and resizing features. This requires that the uploaded temporary image be deleted. Everything works fine, but when I try to delete an image larger than 80px by 80px I get a "File is locked by another process..." error, even though I've released all resources.
Here's a snippet:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone(); //advice from another forum
tempimg.Dispose();
img = resizeImage(img, 200, 200); //delete only works if it's 80, 80
img.Save(newpath);
img.Dispose();
File.Delete(temppath);

I think you are not disposing the first Image instance assigned to the img variable.
Consider this instead:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone();
tempimg.Dispose();
System.Drawing.Image img2 = resizeImage(img, 200, 200);
img2.Save(newpath);
img2.Dispose();
img.Dispose();
File.Delete(temppath);

If you create the image this way, it won't be locked:
using (FileStream fs = new FileStream(info.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] data = new byte[fs.Length];
int read = fs.Read(data, 0, (int)fs.Length);
MemoryStream ms = new MemoryStream(data, false);
return Image.FromStream(ms, false, false); // prevent GDI from holding image file open
}

Related

How to read image inside (as a part of) a stream?

I have files with this structure
+-------------+-------------+---------------+---------+-------------+
| img1_offset | img1_length | Custom Info | Image 1 | Image 2 |
+-------------+-------------+---------------+---------+-------------+
Now I want to read Image 1 to image control. One possible way is open this file in a stream (fileStream), copy image 1 part to other stream (i1_Stream) then read image from i1_Stream. Code I'm using:
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (MemoryStream i1_Stream = new MemoryStream())
{
fileStream.Seek(500, SeekOrigin.Begin); // i1_offset
fileStream.CopyTo(i1_Stream, 30000); // i1_length
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = i1_Stream;
bitmap.EndInit();
return bitmap;
}
}
Because I need open many file like this at one time (ie. load 50 images from 50 files to WrapPanel), I think it is better if I can read Image 1 directly from fileStream. How I can do that? Thank!
First, you should read an array of image bytes from input stream.
Then copy it into new bitmap:
var imageWidth = 640; // read value from image metadata stream part
var imageHeight = 480 // same as for width
var bytes = stream.Read(..) // array length must be width * height
using (var image = new Bitmap(imageWidth, imageHeight))
{
var bitmapData = image.LockBits(new Rectangle(0, 0, imageWidth, imageHeight),
System.Drawing.Imaging.ImageLockMode.ReadWrite, // r/w memory access
image.PixelFormat); // possibly you should read it from stream
// copying
System.Runtime.InteropServices.Marshal.Copy(bytes, 0, bitmapData.Scan0, bitmapData.Height * bitmapData.Stride);
image.UnlockBits(bitmapData);
// do your work with bitmap
}

PDFSharp Locking Tiff Files

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.

convert binary to bitmap using memory stream

Hi I wanna convert binary array to bitmap and show image in a picturebox. I wrote the following code but I got exception that says that the parameter is not valid .
public static Bitmap ByteToImage(byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream);
mStream.Dispose();
return bm;
}
It really depends on what is in blob. Is it a valid bitmap format (like PNG, BMP, GIF, etc?). If it is raw byte information about the pixels in the bitmap, you can not do it like that.
It may help to rewind the stream to the beginning using mStream.Seek(0, SeekOrigin.Begin) before the line Bitmap bm = new Bitmap(mStream);.
public static Bitmap ByteToImage(byte[] blob)
{
using (MemoryStream mStream = new MemoryStream())
{
mStream.Write(blob, 0, blob.Length);
mStream.Seek(0, SeekOrigin.Begin);
Bitmap bm = new Bitmap(mStream);
return bm;
}
}
Don't dispose of the MemoryStream. It now belongs to the image object and will be disposed when you dispose the image.
Also consider doing it like this
var ms = new MemoryStream(blob);
var img = Image.FromStream(ms);
.....
img.Dispose(); //once you are done with the image.
System.IO.MemoryStream mStrm = new System.IO.MemoryStream(your byte array);
Image im = Image.FromStream(mStrm);
im.Save("image.bmp");
Try this. If you still get any error or exception; please post your bytes which you are trying to convert to image. There should be problem in your image stream....

How to crop a tiff image in asp.net

I was looking for a routine by which I can crop tiff image and I got it but it gives many error. Here is the routine:
Bitmap comments = null;
string input = "somepath";
// Open a Stream and decode a TIFF image
using (Stream imageStreamSource = new FileStream(input, FileMode.Open, FileAccess.Read, FileShare.Read))
{
TiffBitmapDecoder decoder = new TiffBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
BitmapSource bitmapSource = decoder.Frames[0];
using (Bitmap b = BitmapFromSource(bitmapSource))
{
Rectangle cropRect = new Rectangle(169, 1092, 567, 200);
comments = new Bitmap(cropRect.Width, cropRect.Height);
//first cropping
using (Graphics g = Graphics.FromImage(comments))
{
g.DrawImage(b, new Rectangle(0, 0, comments.Width, comments.Height),
cropRect,
GraphicsUnit.Pixel);
}
}
}
When I try to compile it, I get an error. I tried adding references to many assemblies searching google but couldn't resolve it. I got this code from this url:
http://snipplr.com/view/63053/
I am looking for advice.
TiffBitmapDecoder class is from Presentation.Core in another words it is from WPF.
BitmapFromSource isn't method of any .net framework class. You can convert BitmapSource to Bitmap using this code.
private Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapsource));
encoder.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}

Saving Panel as an Image

I'm doing this paint application. It's kind of simple. It consist of a panel where I will draw on and then finally I will save as JPG or BMP or PNG file.
My application work perfectly but the problem I'm facing is that when I'm saving the output is not what drawn on the panel its black Image nothing just black.
all my work is been saved as
Thepic = new Bitmap(panel1.ClientRectangle.Width, this.ClientRectangle.Height);
and on the mouse (down,up thing) I have
snapshot = (Bitmap)tempDraw.Clone();
and it saved the work normally but again the rsult is black Image not what the panel contain.
I think the problem may be that you're using the "Clone" method.
Try "DrawToBitmap" - that's worked for me in the past.
Here's a sample that saves a bitmap from a control called "plotPrinter":
int width = plotPrinter.Size.Width;
int height = plotPrinter.Size.Height;
Bitmap bm = new Bitmap(width, height);
plotPrinter.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
bm.Save(#"D:\TestDrawToBitmap.bmp", ImageFormat.Bmp);
Be aware of saving directly to the C directly as this is not
permitted with newer versions of window, try using SaveFileDialog.
SaveFileDialog sf = new SaveFileDialog();
sf.Filter = "Bitmap Image (.bmp)|*.bmp|Gif Image (.gif)|*.gif|JPEG Image (.jpeg)|*.jpeg|Png Image (.png)|*.png|Tiff Image (.tiff)|*.tiff|Wmf Image (.wmf)|*.wmf";
sf.ShowDialog();
var path = sf.FileName;
You could try this, this works for me.
I used MemoryStream.
MemoryStream ms = new MemoryStream();
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height));
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); //you could ave in BPM, PNG etc format.
byte[] Pic_arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(Pic_arr, 0, Pic_arr.Length);
ms.Close();

Categories