Cannot access a closed Stream? How to prevent it? - c#

I am trying to run this code but I always get an error "cannot access a closed Stream"
private MemoryStream GetBitmap(Stream stream, int width, int height)
{
const int size = 150;
const int quality = 75;
using (var image = new Bitmap(System.Drawing.Image.FromStream(stream)))
{
//int width, height;
if (image.Width > image.Height)
{
width = size;
height = Convert.ToInt32(image.Height * size / (double)image.Width);
}
else
{
width = Convert.ToInt32(image.Width * size / (double)image.Height);
height = size;
}
var resized = new Bitmap(width, height);
using (var graphics = Graphics.FromImage(resized))
{
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.DrawImage(image, 0, 0, width, height);
using (var output = new MemoryStream())
{
var qualityParamId = Encoder.Quality;
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(qualityParamId, quality);
var codec = ImageCodecInfo.GetImageDecoders()
.FirstOrDefault(codec => codec.FormatID == ImageFormat.Jpeg.Guid);
resized.Save(output, codec, encoderParameters);
return output;
}
}
}
}
Not sure how can I prevent Memorystream output from closing a Stream stream.
EDIT
After your suggestions here is the new code
public Stream GetBitmap(Stream stream)
{
using (var image = SystemDrawingImage.FromStream(stream))
{
var scaled = ScaledSize(image.Width, image.Height, ThumbnailSize);
var resized = new Bitmap(scaled.width, scaled.height);
using (var graphics = Graphics.FromImage(resized))
using (var attributes = new ImageAttributes())
{
attributes.SetWrapMode(WrapMode.TileFlipXY);
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.AssumeLinear;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(image, Rectangle.FromLTRB(0, 0, resized.Width, resized.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
using (var encoderParams = new EncoderParameters(1))
using (var qualityParam = new EncoderParameter(Encoder.Quality, (long)Quality))
{
encoderParams.Param[0] = qualityParam;
var streamOutput = new MemoryStream();
resized.Save(streamOutput, systemDrawingJpegCodec, encoderParams);
return streamOutput;
}
}
}
}
Here is the controller action method
[HttpGet("{photoId}")]
public async Task<IActionResult> GetPhoto(intphotoId)
{
if (String.IsNullOrEmpty(photoid))
return NotFound();
var response = await _service.GetPhotoAsync(photoId);
Response.Headers.Add("Content-Disposition", new ContentDisposition()
{
FileName = "test.jpg",
Inline = true
}.ToString());
return File(response, "image/jpeg");
}

You're creating the MemoryStream in a using block. When the block goes out of scope the Dispose method is being called. Just remove the using so that it isn't closed:
var output = new MemoryStream())
var qualityParamId = Encoder.Quality;
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(qualityParamId, quality);
var codec = ImageCodecInfo.GetImageDecoders()
.FirstOrDefault(codec => codec.FormatID == ImageFormat.Jpeg.Guid);
resized.Save(output, codec, encoderParameters);
return output;
Also, if you're planning to have the caller read the stream then you'll probably want to reset the Position property so that anyone reading from it starts from the beginning:
output.Position = 0;
return output;

Related

Crashes, OutOfMemoryException, generic error when using System.Drawing

I am running a cron job in C# that takes 200,000 images and convert into 1 bit image. During running this job, sometimes the process crashes (even though I have global try catch), sometimes for some images(not all) it throws OutOfMemoryException and sometimes for some images it it throws A generic error occurred in GDI.
int pageSize = 1000;
for (int pageNumber = 0; pageNumber < 200; pageNumber++)
{
var imageUrls = allIMageUrls.Skip(pageSize * pageNumber).Take(pageSize).ToList();
var counter = 0;
var total = imageUrls.Count;
Logger.Log($"Page Number : {pageNumber}");
var failedImageUrls = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imageUrls, imageUrl =>
{
try
{
Interlocked.Increment(ref counter);
var image = _httpService.DownloadImage(imageUrl);
if (image != null && image.Length > 0)
{
var oneBitImage = ConvertToOnebitFaxGroup4(contract);
_httpService.UploadImage(image, oneBitImage);
oneBitImage = null;
image = null;
}
}
catch (Exception ex)
{
failedImageUrls.Add(imageUrl);
Logger.Log(ex);
}
});
This is one time process. I added paging so that when it crashes I can restart from that page instead of start at beginning.
public static class ImageProcessor
{
static ImageCodecInfo _codecInfo;
static EncoderParameters _encoderParameters;
static ImageProcessor()
{
foreach (var codec in ImageCodecInfo.GetImageEncoders())
{
if (codec.MimeType == "image/tiff")
{
_codecInfo = codec;
break;
}
}
_encoderParameters = new EncoderParameters(2);
_encoderParameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
_encoderParameters.Param[1] = new EncoderParameter(Encoder.ColorDepth, (long)1);
}
public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
var image = Image.FromStream(memoryStream);
var pData = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, pData, bytes.Length);
var bytesPerLine = (image.Width + 31) / 32 * 4;
var img = new Bitmap(image.Width, image.Height, bytesPerLine, PixelFormat.Format1bppIndexed, pData);
using (var ms = new MemoryStream())
{
image.Save(ms, _codecInfo, _encoderParameters);
img.Dispose();
Marshal.FreeHGlobal(pData);
return ms.ToArray();
}
}
}
Updated:
public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
using (var image = Image.FromStream(memoryStream))
{
var pData = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, pData, bytes.Length);
var bytesPerLine = (image.Width + 31) / 32 * 4;
using (var img = new Bitmap(image.Width, image.Height, bytesPerLine, PixelFormat.Format1bppIndexed, pData))
{
using (var ms = new MemoryStream())
{
img.Save(ms, _codecInfo, _encoderParameters);
Marshal.FreeHGlobal(pData);
return ms.ToArray();
}
}
}
}
}
Update2
public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
using (var image = Image.FromStream(memoryStream))
{
using (var ms = new MemoryStream())
{
image.Save(ms, _codecInfo, _encoderParameters);
return ms.ToArray();
}
}
}
}

Attempt to save to stream as PNG I get "Expression has been evaluated and has no value void"

I am trying to create API in .net core 2.0 to convert string to base64 image Qrcode as below.
I am currently using "ZXing.net" and "CoreCompat.System.Drawing" nugget packages in my project. When I tried to save to stream as PNG
i.e. "bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png) I get Expression has been evaluated and has no value void"
I will appreciate your help. Thanks in advance
` var ss = "123longstringtoencodedhhhh34444hhh123";
var qr = new ZXing.BarcodeWriterPixelData
{
Format = ZXing.BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions { Height = 250, Width = 250, Margin = 0 }
};
var pixelData = qr.Write(ss);
using (var bitmap = new System.Drawing.Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb))
using (var ms = new MemoryStream())
{
var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, pixelData.Width, pixelData.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
try
{
// we assume that the row stride of the bitmap is aligned to 4 byte multiplied by the width of the image
System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0,
pixelData.Pixels.Length);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
// trying to save to stream as PNG
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
}
error
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png) Expression has been evaluated and has no value void`
Note: Two Dependencies are required which can be installed as nuget package from Nuget Package Manager:
CoreCompat.System.Drawing
ZXing.Net
I got the error message because I missed this line
"var pixelData = qr.Write(Encoding.Default.GetString(ss));"
var ss = "123longstringtoencodedhhhh34444hhh123";
See complete code below
var qr = new ZXing.BarcodeWriterPixelData()
{
Format = ZXing.BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions { Height = 250, Width = 250, Margin = 0 }
};
var pixelData = qr.Write(Encoding.Default.GetString(ss));
using (var bitmap = new Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb))
using (var ms = new MemoryStream())
{
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, pixelData.Width, pixelData.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
try
{
System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0, pixelData.Pixels.Length);
}
finally
{
bitmap.UnlockBits(bitmapData);
}
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return Convert.ToBase64String(ms.ToArray())
}
}

get gdi+ error on calling wpf dll method from windows form application on c#

i've created a wpf class library dll to apply an shader effect to a System.Drawing.Bitmap. A method of this class will be called from a "normal" non wpf windows application async callback method (TCPServer send method).
The method will give me back a Bitmap and seems to working withou any issues. When i will call the Bitmap.Save(...) method i will get a "generic error in gdi+" error. Can anyone see what i've doing wrong?
The WPF Class Library DLL:
public static System.Drawing.Bitmap ApplyShaderToBitmap(System.Drawing.Bitmap Source, String FilenamePS)
{
System.Drawing.Bitmap returnBitmap = null;
Thread STAThread = new Thread(() =>
{
try
{
returnBitmap=Source;
double WPF_DPI_X = 96.0;
double WPF_DPI_Y = 96.0;
PixelShader ps = new PixelShader();
ps.UriSource = new Uri(FilenamePS);
LenseCorrectionEffect lce = new LenseCorrectionEffect(ps);
//Finally, apply the shader effect to img.
Image img = new Image();
img.Stretch = Stretch.None;
img.Effect = lce;
Viewbox viewbox;
viewbox = new Viewbox();
viewbox.Stretch = Stretch.None;
viewbox.Child = img;
img.BeginInit();
img.Width = Source.Width;
img.Height = Source.Height;
BitmapSource xx= BitmapToBitmapSource.ToBitmapSource(Source);
img.Source = xx;
img.EndInit();
viewbox.Measure(new Size(img.Width, img.Height));
viewbox.Arrange(new Rect(0, 0, img.Width, img.Height));
viewbox.UpdateLayout();
using (MemoryStream outStream = new MemoryStream())
{
//PngBitmapEncoder encoder = new PngBitmapEncoder();
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
//RenderTargetBitmap bitmap = new RenderTargetBitmap((int)(img.Width * Source.DpiX / WPF_DPI_X), (int)(img.Height * Source.DpiY / WPF_DPI_Y), Source.DpiX, Source.DpiY, PixelFormats.Pbgra32);
RenderTargetBitmap bitmap = new RenderTargetBitmap(Convert.ToInt32(img.Width), Convert.ToInt32(img.Height), Convert.ToInt32(WPF_DPI_X), Convert.ToInt32(WPF_DPI_Y), PixelFormats.Pbgra32);
bitmap.Render(viewbox);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
encoder.Save(outStream);
returnBitmap = new System.Drawing.Bitmap(outStream);
encoder = null;
bitmap = null;
frame = null;
}
xx = null;
viewbox = null;
img = null;
lce = null;
ps = null;
}
catch (Exception ex)
{
returnBitmap = null;
}
});
STAThread.SetApartmentState(ApartmentState.STA);
STAThread.Start();
STAThread.Join();
STAThread = null;
return returnBitmap;
} // ApplyShaderToBitmap
The Calling from a async callback method:
bm = LenseCorrection.Shader.ApplyShaderToBitmap(bm, ShaderFilename);
bm.Save(ms, jgpEncoder, myEncoderParameters);
Thanks forward

Issue / Image Corruption Using Graphics.DrawImage With Task Parallel Library?

I have some code which takes an uploaded image, resizes it to 5 different sizes, then uploads those to another storage repository. I'm trying to execute the 5 image resizing operations in parallel using TPL.
Upfront I'll mention that the resizing function is a static method, but that it doesn't use any static resources (so multiple parallel calls shouldn't be stepping on each other, from what I can tell). Also very relevant is that this is in the context of ASP.NET which has some different threading concerns.
When I run the following code, I invariably get an "invalid parameter" error from one of the calls, though which call varies:
tasks = new Task<Uri>[]
{
Task.Run<Uri>(() => Upload(intId, ProfilePhotoSize.FiveHundredFixedWidth, photoStream, mediaType, minWidth)),
Task.Run<Uri>(() => Upload(intId, ProfilePhotoSize.Square220, photoStream, mediaType, minWidth)),
Task.Run<Uri>(() => Upload(intId, ProfilePhotoSize.Square140, photoStream, mediaType, minWidth)),
Task.Run<Uri>(() => Upload(intId, ProfilePhotoSize.Square80, photoStream, mediaType, minWidth)),
Task.Run<Uri>(() => Upload(intId, ProfilePhotoSize.Square50, photoStream, mediaType, minWidth))
};
await Task.WhenAll(tasks)
If I look at the images created, some will be ok, some will clearly be corrupted - and this applies not just to the "errored" image.
However, executing those five operations synchronously results in five good images:
Upload(intId, ProfilePhotoSize.FiveHundredFixedWidth, photoStream, mediaType, minWidth);
Upload(intId, ProfilePhotoSize.Square220, photoStream, mediaType, minWidth);
Upload(intId, ProfilePhotoSize.Square140, photoStream, mediaType, minWidth);
Upload(intId, ProfilePhotoSize.Square80, photoStream, mediaType, minWidth);
Upload(intId, ProfilePhotoSize.Square50, photoStream, mediaType, minWidth);
Is there a known issue with these sorts of operations, or have I maybe done something else dodgy that may be causing this?
Here is the image resizing function:
private static Stream Resize(Stream image, ResizeParameters parameters, ImageUtility.ResizeType type)
{
Image image1 = (Image) new Bitmap(image);
Image image2 = (Image) null;
Image image3 = (Image) null;
Graphics graphics = (Graphics) null;
MemoryStream memoryStream = new MemoryStream();
try
{
parameters = ImageUtility.CalculateSize(image1, parameters, type);
if (!parameters.DoNothing)
{
image2 = (Image) new Bitmap(parameters.Width, parameters.Height);
switch (type)
{
case ImageUtility.ResizeType.FixedWidth:
graphics = Graphics.FromImage(image2);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.FillRectangle(Brushes.White, 0, 0, image1.Width, image1.Height);
graphics.DrawImage(image1, 0, 0, parameters.Width, parameters.Height);
graphics.Dispose();
break;
case ImageUtility.ResizeType.PaddingSquare:
image3 = (Image) new Bitmap(parameters.SelectedWidth, parameters.SelectedHeight);
graphics = Graphics.FromImage(image3);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.FillRectangle(Brushes.White, 0, 0, parameters.SelectedWidth, parameters.SelectedHeight);
graphics.DrawImage(image1, parameters.X, parameters.Y, image1.Width, image1.Height);
graphics = Graphics.FromImage(image2);
graphics.DrawImage(image3, 0, 0, parameters.Width, parameters.Height);
graphics.Dispose();
break;
case ImageUtility.ResizeType.CropSquare:
image3 = (Image) new Bitmap(parameters.SelectedWidth, parameters.SelectedHeight);
graphics = Graphics.FromImage(image3);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.FillRectangle(Brushes.White, 0, 0, parameters.SelectedWidth, parameters.SelectedHeight);
graphics.DrawImage(image1, new Rectangle(0, 0, parameters.SelectedWidth, parameters.SelectedHeight), parameters.X, parameters.Y, image3.Width, image3.Height, GraphicsUnit.Pixel);
graphics = Graphics.FromImage(image2);
graphics.DrawImage(image3, 0, 0, parameters.Width, parameters.Height);
graphics.Dispose();
break;
}
EncoderParameter encoderParameter = new EncoderParameter(Encoder.Quality, 90L);
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = encoderParameter;
image2.Save((Stream) memoryStream, ImageUtility.GetEncoder(parameters.Format), encoderParams);
}
else
{
image.Seek(0L, SeekOrigin.Begin);
image.CopyTo((Stream) memoryStream);
}
memoryStream.Seek(0L, SeekOrigin.Begin);
return (Stream) memoryStream;
}
finally
{
image1.Dispose();
if (image2 != null)
image2.Dispose();
if (image3 != null)
image3.Dispose();
if (graphics != null)
graphics.Dispose();
}
}

Image sizing issue in bitmap

I am using the below code to create bitmap. Image is getting created but not with the specified size. Is there any issue with code?
public class ProcessImage : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var bmpOut = new Bitmap(100, 100);
var request = (HttpWebRequest)WebRequest.Create("http://media.zenfs.com/en_us/Sports/ap/201305061639599673916-p2.jpeg");
request.AllowWriteStreamBuffering = true;
var response = (HttpWebResponse) request.GetResponse();
var myStream = response.GetResponseStream();
var b = new Bitmap(myStream);
var g = Graphics.FromImage(bmpOut);
g.InterpolationMode = InterpolationMode.High;
g.DrawImage(b,0,0,50, 50);
context.Response.ContentType = "image/png";
var stream = new MemoryStream();
b.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
b.Dispose();
stream.WriteTo(HttpContext.Current.Response.OutputStream);
//context.Response.Write("Hello World");
}
private Bitmap ResizeImage(Image image, int width, int height)
{
Bitmap result = new Bitmap(width, height);
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(result))
{
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
return result;
}

Categories

Resources