2010-05-10 28 views
12

如何以原始编码保存图像?如何以原始格式保存图像?

看来,保存图像的唯一方法是使用BitmapEncoder,但我不知道如何从图像中获取正确的格式。

实施例:

Clipboard.GetImage()返回一个InteropBitmap这似乎不包含关于原始格式的任何信息。

我也使用扩展方法尝试:

public static void Save(this BitmapImage image, System.IO.Stream stream) 
{ 
    var decoder = BitmapDecoder.Create(image.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 
    var encoder = BitmapEncoder.Create(decoder.CodecInfo.ContainerFormat); 
    foreach (var frame in decoder.Frames) 
    { 
     encoder.Frames.Add(BitmapFrame.Create(frame)); 
    } 
    encoder.Save(stream); 
} 

但问题是

  1. 所述的ImageSource并不总是一个的BitmapImage(Clipboard.GetImage())例如和
  2. image.StreamSource在某些情况下可能为空(似乎是通过相对Uri加载图像时)

有什么建议吗?

+0

你能解释一下你正在经历的情况吗?在我看来,如果您要保存图像,只需将其保存为所需的格式即可。 BitmapImage似乎是一种格式,原始图像已被转换。 – 2010-08-04 22:16:11

回答

19

这里最根本的问题是“我如何取一个任意的ImageSource并学习它最初编码的格式?”

答案是,你不能总是这样做,但对于大多数实际目的来说,有一个解决方法可用。如上面的代码所示,一旦你了解了使用哪种格式,其余部分都很容易。

你不能总能找出原始格式的原因是,它有可能(用一些非常棘手的代码!)来子类化BitmapSource来创建一个新的ImageSource,从你喜欢的任何地方获取它的数据。例如,我可以实现一个返回随机像素的PseudoRandomBitmapSource。在这种情况下,“原始格式”可能是用于随机数生成器的种子。

如果你正在处理的内置的ImageSource类之一,找出原来的编码方式取决于它的确切类,你正在使用:

  1. 对于BitmapImage的,你可以使用的StreamSource或UriSource ,无论哪一个设置。其中任何一个都可以传递给BitmapDecoder.Create重载。您的示例代码显示了如何为StreamSource执行此操作。这与UriSource完全相同,除非您需要新的Uri(BaseUri, UriSource)并将其传递给需要Uri的BitmapDecoder.Create过载。

  2. 对于ColorConvertedBitmap,CroppedBitmap,FormatConvertedBitmap和TransformedBitmap,可以使用公共“Source”属性获取底层源代码,然后使用此算法递归检查其编码。

  3. 对于CachedBitmap,您只能通过内部字段获取源位图。如果您拥有足够的权限,则可以使用反射访问,否则您的运气不佳。

  4. 对于RenderTargetBitmap,WritableBitmap,D3DImage和DrawingImage,由于图像是从矢量格式或算法“实时”构建的,因此没有原始编码。

  5. 对于BitmapFrame,使用Decoder属性获取解码器,然后使用CodecInfo.ContainerFormat。

  6. 对于InteropBitmap或UnmanagedBitmapWrapper,它非常复杂。基本上你需要使用反射来读取内部的WicSourceHandle属性,然后调用DangerousGetHandle()来获得一个实际上是非托管IUnkown的IntPtr。 IWICBitmapDecoder使用非托管代码,QueryInterface。如果成功,您可以调用IWICBitmapDecoder.GetContainerFormat获取格式Guid(这仍然是非托管代码)。如果没有,关于原始来源的所有信息都已丢失。

正如你所看到的,还有相当,你不能得到源(例如,对于完全解码位图的InteropBitmap),或者获得源需要专门的技术和特权(对于非托管代码少数情况下InteropBitmap或CachedBitmap内部字段的反射)。

因为通常很难不可能获得原始格式,所以寻找保存这些信息的方法是一个好主意,因为它会传递到您的代码中。如果控制图像的源,其可以是作为创建ImageSourceAndFormat类如简单:

public class BitmapSourceAndFormat 
{ 
    public ImageSource Source { get; set; } 
    public Guid OriginalFormat { get; set; } 
} 

此,如果要放置在图像上剪贴板是特别有用的。通常,除了将图像添加到DataObject之外,您还可以添加一个BitmapSourceAndFormat对象。如果这样做,则粘贴操作将接收包含这两种格式的DataObject。您的代码只需要首先检查BitmapSourceAndFormat对象。如果发现它可以简单地访问它以获得原始格式。如果不是,它必须诉诸于上述手段。

最后一个注意事项:对于剪贴板粘贴,您可以检查可用的数据格式字符串。一些有用的是:Bitmap,Dib,Tiff,Gif,Jpeg和FileName。对于“Tiff”,“Gif”和“Jpeg”,您可以硬编码所需的格式。对于“文件名”,您可以自己打开文件以获取流传递给BitmapDecoder。对于没有其他任何东西的“Dib”或“Bitmap”,您知道原始格式已丢失。

+0

哇 - 这是一个很好的答案。非常感谢!有关复制/粘贴的重要提示! – 2010-08-05 01:17:25

+0

+感谢不仅告诉我我想知道什么,而且还告诉我我的根本问题是什么!我应该更好地描述它。 – 2010-08-05 01:20:49

相关问题