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

时间:2010-05-10 06:48:37

标签: c# wpf imaging

如何使用原始编码保存图像?

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

示例:

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

我也尝试过使用Extension方法:

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可以为null(似乎是通过相对Uri加载图像时)
  3. 有什么建议吗?

1 个答案:

答案 0 :(得分: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()来获得一个IntPtr,它实际上是一个非托管的IUnkown。使用非托管代码,IWICBitmapDecoder的QueryInterface。如果成功,您可以调用IWICBitmapDecoder.GetContainerFormat来获取格式Guid(这仍然是非托管代码)。如果没有,则有关原始来源的所有信息都已丢失。

  7. 正如您所看到的,有很多情况下您无法获取源(例如,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”,您可以对所需格式进行硬编码。对于“FileName”,您可以自己打开文件以获取要传递给BitmapDecoder的流。对于没有任何其他内容的“Dib”或“Bitmap”,您知道原始格式已丢失。