C# Image.Save ends in "Invalid Bmp" - c#

I try to create a memory stream of an object which is indeed a System.Drawing.Bitmap. The MemoryStream is later on saved on the disk as a temporary file.
This is the code:
object resource = TestData.ResourceManager.GetObject(key);
...
if (resource is System.Drawing.Bitmap)
{
MemoryStream memoryStream = new MemoryStream();
Encoder myEncoder = System.Drawing.Imaging.Encoder.ColorDepth;
EncoderParameters myEncoderParameters = new EncoderParameters();
myEncoderParameters.Param[0] = new EncoderParameter(myEncoder, 16);
((System.Drawing.Bitmap)resource).Save(memoryStream, ImageFormat.Bmp);
return memoryStream;
}
...
The memory stream is then saved:
string targetFilePath = Path.GetTempFileName();
using (MemoryStream resourceStream = ResolveAsStream(key))
{
if (resourceStream == null)
{
throw new InvalidOperationException("Could not find resource for key '" + key + "'.");
}
using (FileStream fs = new FileStream(targetFilePath, FileMode.Open, FileAccess.Write))
{
resourceStream.CopyTo(fs);
}
}
return targetFilePath;
The file is successfully created but when I try to open it, it tells me, that the image file is invalid (i.e. it cannot be opened by Paint or any other image viewer). Instead of using ImageFormat.Bmp I tried with ImageFormat.MemoryBmp:
object resource = TestData.ResourceManager.GetObject(key);
...
if (resource is System.Drawing.Bitmap)
{
MemoryStream memoryStream = new MemoryStream();
Encoder myEncoder = System.Drawing.Imaging.Encoder.ColorDepth;
EncoderParameters myEncoderParameters = new EncoderParameters();
myEncoderParameters.Param[0] = new EncoderParameter(myEncoder, 16);
((System.Drawing.Bitmap)resource).Save(memoryStream, ImageFormat.MemoryBmp);
return memoryStream;
}
...
But this actually fails with
"value Cannot be null Parameter name: encoder"
It seems there is no codec specified for MemoryBmp. So how can I save the image correctly so that the Bmp file is valid?

Related

How can i clear the memory after converting BitmapImage to Byte

I have two functions: one to convert from image to byte and other to convert from byte to bitmapImage.
So, when I open the window with that images, I convert from byte to bitmapImage and it works great, but when I close and open it again it just keeps on memory and if I continue to do that time and time again it just throws an exception Out Of Memory exception
Image to byte->
private byte[] ConvertImageToBinary(Image img)
{
using (MemoryStream ss = new MemoryStream())
{
img.Save(ss, System.Drawing.Imaging.ImageFormat.Jpeg);
var s = ss.ToArray();
var jpegQuality = 50;
Image image;
using (var inputStream = new MemoryStream(s))
{
image = Image.FromStream(inputStream);
var jpegEncoder = System.Drawing.Imaging.ImageCodecInfo.GetImageDecoders()
.First(c => c.FormatID == System.Drawing.Imaging.ImageFormat.Jpeg.Guid);
var encoderParameters = new System.Drawing.Imaging.EncoderParameters(1);
encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, jpegQuality);
Byte[] outputBytes;
using (var outputStream = new MemoryStream())
{
image.Save(outputStream, jpegEncoder, encoderParameters);
return outputBytes = outputStream.ToArray();
}
}
}
}
Byte to bitmap ->
public BitmapImage ConvertBinaryToImage(byte[] array)
{
var image = new BitmapImage();
using (var ms = new MemoryStream(array))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
image.Freeze();
}
return image;
}
When I open the WindowDragAndDrop it loads all the images
But when I close it it still uses the same amount of memory
Image is indeed disposable (https://learn.microsoft.com/en-us/dotnet/api/system.drawing.image?view=netframework-4.8), so you also need:
using (var image = Image.FromStream(inputStream)){
}
Around everywhere you use Image objects.

Cannot read a Bitmap image that I just saved in Base64

I try to load a picture (PNG), save-it in Base64 in a text file and reload it, but I only see gliberish pictures (black and white, very ugly, far from original!) after I load the picture from the text file.
Where's my problem?
BTW all examples (load the picture from image file, save to base64, load from base64) are all taken from SO questions.
First it's how a load the pictures from the PNG file:
try
{
var openFileDialog = new OpenFileDialog
{
CheckFileExists = true,
Multiselect = false,
DefaultExt = "png",
InitialDirectory =
Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
};
if (openFileDialog.ShowDialog() == true)
{
Bitmap img;
using (var stream = File.Open(openFileDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
img = new Bitmap(stream);
}
Logo.Source = BitmapToImageSource(img);
}
}
catch (Exception exception)
{
MessageBox.Show(exception.ToString(), "An error occured", MessageBoxButton.OK, MessageBoxImage.Warning);
}
Save it to base64:
try
{
Bitmap img = BitmapSourceToBitmap2((BitmapSource) Logo.Source);
string base64String;
using (var stream = new MemoryStream())
{
img.Save(stream, ImageFormat.Png);
byte[] imageBytes = stream.ToArray();
base64String = Convert.ToBase64String(imageBytes);
}
string fileName = string.Format(CultureInfo.InvariantCulture, "image{0:yyyyMMddHHmmss}.txt",
DateTime.Now);
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName);
using (var stream = File.Open(path, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
using (var writer = new StreamWriter(stream, System.Text.Encoding.UTF8))
{
writer.Write(base64String);
writer.Flush();
}
}
}
catch (Exception exception)
{
MessageBox.Show(exception.ToString(), "An error occured", MessageBoxButton.OK, MessageBoxImage.Warning);
}
BitmapSourceToBitmap2:
int width = srs.PixelWidth;
int height = srs.PixelHeight;
int stride = width*((srs.Format.BitsPerPixel + 7)/8);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(height*stride);
srs.CopyPixels(new Int32Rect(0, 0, width, height), ptr, height*stride, stride);
using (var btm = new Bitmap(width, height, stride, PixelFormat.Format1bppIndexed, ptr))
{
// Clone the bitmap so that we can dispose it and
// release the unmanaged memory at ptr
return new Bitmap(btm);
}
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
And load it back from the file:
try
{
var openFileDialog = new OpenFileDialog
{
CheckFileExists = true,
Multiselect = false,
DefaultExt = "txt",
InitialDirectory =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
};
if (openFileDialog.ShowDialog() == true)
{
string base64String;
using (FileStream stream = File.Open(openFileDialog.FileName, FileMode.Open))
{
using (var reader = new StreamReader(stream))
{
base64String = reader.ReadToEnd();
}
}
byte[] binaryData = Convert.FromBase64String(base64String);
var bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = new MemoryStream(binaryData);
bi.EndInit();
Logo.Source = bi;
}
}
catch (Exception exception)
{
MessageBox.Show(exception.ToString(), "An error occured", MessageBoxButton.OK, MessageBoxImage.Warning);
}
Here is a short code sequence that reads a JPG file into a byte array, creates a BitmapSource from it, then encodes it into a base64 string and writes that to file.
In a second step, the base64 string is read back from the file, decoded and a second BitmapSource is created.
The sample assumes that there is some XAML with two Image elements named image1 and image2.
Step 1:
var imageFile = #"C:\Users\Clemens\Pictures\DSC06449.JPG";
var buffer = File.ReadAllBytes(imageFile);
using (var stream = new MemoryStream(buffer))
{
image1.Source = BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
var base64File = #"C:\Users\Clemens\Pictures\DSC06449.b64";
var base64String = System.Convert.ToBase64String(buffer);
File.WriteAllText(base64File, base64String);
Step 2:
base64String = File.ReadAllText(base64File);
buffer = System.Convert.FromBase64String(base64String);
using (var stream = new MemoryStream(buffer))
{
image2.Source = BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
In case you need to encode an already existing BitmapSource into a byte array, use code like this:
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
buffer = stream.ToArray();
}

Compress image without saving it

I am using the following code to compress an image and it does a nice job but I want to use the compressed image not save it. So right now I have to save the image then read it in again which is slow. Is there a way of compressing it with out saving it.
private void compress(System.Drawing.Image img, long quality, ImageCodecInfo codec)
{
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
img.Save("check1.jpg", codec, parameters);
}
private static ImageCodecInfo GetCodecInfo(string mimeType)
{
foreach (ImageCodecInfo encoder in ImageCodecInfo.GetImageEncoders())
if (encoder.MimeType == mimeType)
return encoder;
throw new ArgumentOutOfRangeException(
string.Format("'{0}' not supported", mimeType));
}
There is an overload that takes a Stream so you can save it straight to a MemoryStream and won't need to save to disk/reload.
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
var ms = new MemoryStream();
img.Save(ms, codec, parameters);
//Do whatever you need to do with the image
//e.g.
img = Image.FromStream(ms);
The reason you're getting the "Parameter not valid" exception you mention in the comments is because the image isn't being disposed of before you try to call FromStream, so you'll need to dispose it. Also, I don't know how you're calling this method, but you should probably update it to return the MemoryStream.
private MemoryStream compress(System.Drawing.Image img, long quality, ImageCodecInfo codec)
{
EncoderParameters parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
var ms = new MemoryStream();
img.Save(ms, codec, parameters);
return ms;
}
public void MyMethod()
{
MemoryStream ms;
using(var img = Image.FromFile("myfilepath.img"))
{
ms = compress(img, /*quality*/, /*codec*/);
}
using(var compressedImage = Image.FromStream(ms))
{
//Use compressedImage
}
}
Notice how I return ms from compress and capture it. Also, more importantly, how we wrap the initial img in a using statement which will dispose the file handle correctly, and after that gets disposed create the second compressedImage which is also in a using so it will also get disposed of properly when you're done.

Cannot save Bitmapsource to Bitmapimage

I am trying to save an image from a users Clipboard. I am able to get all the correct data from the clipboard image into the Bitmapsource. I am trying to save to a Bitmapimage so I can upload a file to a website. When converting the Bitmapsource to a Bitmapimage, all of the Bitmapimages data stays null and will throw an exception.
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if ((Keyboard.Modifiers == ModifierKeys.Control) && (e.Key == Key.V))
{
if (Clipboard.ContainsImage())
{
BitmapSource bitmapSource = Clipboard.GetImage();
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
MemoryStream memoryStream = new MemoryStream();
BitmapImage bImg = new BitmapImage();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memoryStream);
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(memoryStream.ToArray());
bImg.EndInit();
var client = new WebClient();
var uri = bImg.UriSource;
var path = uri.AbsolutePath;
//client.UploadFile(link, path);
}
}
}
Both
var uri = bImg.UriSource;
var path = uri.AbsolutePath;
Will throw an unhandled exception of type 'System.NullReferenceException' occurred in WpfApplication1.exe
Additional information: Object reference not set to an instance of an object
Creating a BitmapImage from its StreamSource property will not magically set its UriSource property. You do not need that BitmapImage at all.
Write the original BitmapSource to a FileStream instead of a MemoryStream, and upload that file:
string path = ...
using (var fileStream = new FileStream(path, FileMode.Create))
{
encoder.Save(fileStream);
}
client.UploadFile(link, path);
It may even be unnecessary to create an intermediate file. Just upload the buffer of the MemoryStream:
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
client.UploadData(link, memoryStream.ToArray());
}

Bitmap from Stream: Bug?

I have a pretty strange Error. I need to scale down images, scale down the quality and convert to JPEG. This all works when I save the File on disk, but it doesn't work when I save it to a Stream.
System.Drawing.Bitmap bitmap = // valid Bitmap from Disk
System.IO.Stream stream = new MemoryStream();
// JPEG Encoding
System.Drawing.Imaging.ImageCodecInfo jpgEncoder = GetEncoder( System.Drawing.Imaging.ImageFormat.Jpeg );
System.Drawing.Imaging.Encoder encoder2 = System.Drawing.Imaging.Encoder.Quality;
System.Drawing.Imaging.EncoderParameters parameters = new System.Drawing.Imaging.EncoderParameters( 1 );
System.Drawing.Imaging.EncoderParameter parameter = new System.Drawing.Imaging.EncoderParameter( encoder2, qualityLevel );
parameters.Param[0] = parameter;
// Save downscaled on Disk and stream
bitmap.Save( stream, jpgEncoder, parameters );
bitmap.Save( #"C:\TestJPEG.jpg", jpgEncoder, parameters );
// some stream stuff
var bytes = ((MemoryStream)stream).ToArray();
System.IO.Stream inputStream = new MemoryStream( bytes );
// Load from disk and stream
Bitmap fromDisk = new Bitmap( #"C:\TestJPEG.jpg" ); // works
Bitmap fromStream = new Bitmap( inputStream ); // crash invalid parameter no inner message or description
Bitmap fromStream2 = (Bitmap)Bitmap.FromStream( inputStream ); // same error here
// also crashes if I load the "stream" named Stream
I can also open the converted file with Paint.
Any suggestions?
Edit:
I'm using .Net Framework 4.0 on Windows 7 Professional
Edit2:
Tried that Seek thing (Answer was deleted)
stream.Seek( 0, SeekOrigin.Begin );
It workes with the "old" stream. But i need to load it from a byte Array. Still same crash
The following code works for me:
var bitmap = new Bitmap(#"c:\Dokumente und Einstellungen\daniel.hilgarth\Desktop\Unbenannt.bmp");
ImageCodecInfo jpgEncoder = ImageCodecInfo.GetImageEncoders().Single(x => x.FormatDescription == "JPEG");
Encoder encoder2 = System.Drawing.Imaging.Encoder.Quality;
EncoderParameters parameters = new System.Drawing.Imaging.EncoderParameters( 1 );
EncoderParameter parameter = new EncoderParameter( encoder2, 50L );
parameters.Param[0] = parameter;
System.IO.Stream stream = new MemoryStream();
bitmap.Save( stream, jpgEncoder, parameters );
bitmap.Save(#"C:\Temp\TestJPEG.jpg", jpgEncoder, parameters);
var bytes = ((MemoryStream)stream).ToArray();
System.IO.Stream inputStream = new MemoryStream(bytes);
Bitmap fromDisk = new Bitmap(#"C:\Temp\TestJPEG.jpg");
Bitmap fromStream = new Bitmap(inputStream);
There are a few differences to your code. Which one causes your problem is up to you to find out, I guess:
I used 50L as qualityLevel. When using 1, 2, 50 or 100, I was getting an ArgumentException "Parameter is not valid". As I don't know the type or value of your qualityLevel variable that can very well be the problem.
I replaced your GetEncoder method. I don't know what your method does exactly, so it could be the problem, but I doubt it.

Categories