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 加载图像时)

有什么建议么?

4

1 回答 1

20

这里的基本问题是“我如何获取任意 ImageSource 并了解它最初编码的格式?”

答案是您不能总是这样做,但对于大多数实际目的,有一个可用的解决方法。正如您上面的代码所示,一旦您了解了使用哪种格式,剩下的就很容易了。

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

如果您正在处理内置的 ImageSource 类之一,则找出原始编码的方法取决于您使用的确切类:

  1. 对于 BitmapImage,您可以使用 StreamSource 或 UriSource,无论设置哪一个。其中任何一个都可以传递给 BitmapDecoder.Create 重载。您的示例代码显示了如何为 StreamSource 执行此操作。它与 UriSource 完全相同,只是您需要 newUri(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”,您可以硬编码所需的格式。对于“FileName”,您可以自己打开文件以获取要传递给 BitmapDecoder 的流。对于没有任何其他内容的“Dib”或“Bitmap”,您知道原始格式已丢失。

于 2010-08-05T01:00:33.183 回答