views:

225

answers:

5

I am embedding images into my assembly using .resx files. Upon runtime, I need to save the images into standalone files, restoring the original content. How can I extract the original file stream from an System.Drawing.Bitmap instance? I know I can create a stream using Bitmap.Save(), but this transcodes (and in effect - inflates) the images, even when saving a PNG back as PNG.

Or perhaps my mistake is reading them from Resource as Bitmap in the first place?

+1  A: 

If you're not actually using the Bitmap for display purposes in your appliction (i.e. you're just extracting the resource as a bitmap so you can save it as a file), then the simplest approach is to just get the resource as a Stream and save it to a file.

This tutorial shows how to get resources as streams:

http://devhood.com/Tutorials/tutorial_details.aspx?tutorial_id=75

In the sample, the author gets a stream and passes it to the Bitmap.FromStream method. What you would do instead is just save that stream directly to a file (bypassing Bitmap entirely).

MusiGenesis
+1  A: 

Yes, once you have read it as a Bitmap object, you can't get the original file stream back. The Bitmap object only contains the uncompressed data, not the original data.

You should read the resource as byte data intead. You can read from the resource stream and write to a file:

using (Stream source = this.GetType().Assembly.GetManifestResourceStream("WindowsApplication1.filename.jpg")) {
  using (FileStream dest = File.Create(fileName)) {
    byte[] buffer = new byte[4096];
    while (true) {
      int len = source.Read(buffer, 0, buffer.Length);
      if (len == 0) break;
      dest.Write(buffer, 0, len);
    }
  }
}
Guffa
+1  A: 

I don't get the problem. PNG is a loss-less format, you'll get the exact same image back. Yes, you don't necessarily get the exact same bytes when you Save(). It is a compressed format and the amount of time spent by the compressor on getting the best balance between compression and speed might not be the same. But, so what?

If it is a real problem then you shouldn't add the image as a managed resource. You can add it to your project as an embedded resource and read it from the metadata with Assembly.GetManifestResourceStream(). You'll get the raw image file bytes. Convertible to an Image with Image.FromStream().

Hans Passant
I'm also storing .jpg and .gif, and the image files are optimized quite heavily by the graphics designer. But the solution I use now (and which works) is based on embedded resources instead of managed, and as you wrote - this works.
skolima
@skolima: great, please close your thread by marking it answered.
Hans Passant
A: 

It turns out the problem was in using managed resources - resx builder creates Bitmap objects from image files and serializes those objects, it does not store the original file stream. However, embedded resources can store flat binary files.

I am now embedding all files from my images folder using the following .csproj entries:

<EmbeddedResource Include="Images\*.jpg" />
<EmbeddedResource Include="Images\*.png" />
<EmbeddedResource Include="Images\*.gif" />

Visual Studio shows them as normal in-project files with Build Action set to Embedded Resource.

Then, from my code, I load the original file streams with:

var assembly = GetType().Assembly;
var regex = new Regex("\\.(png|jpg|gif))$");
foreach (var bitmap in assembly.GetManifestResourceNames())
{
  if(!regex.IsMatch(bitmap)
    continue;

  var stream = assembly.GetManifestResourceStream(bitmap);
  //handle the stream here
}
skolima