是否可以在不重新编码的情况下保留用于构建 BitmapImage 的原始数据?

Is it possible to persist the original data used to build a BitmapImage without reencoding?

再详细说明一下标题,我们有一些代码可以从磁盘加载图像文件,然后将其转换为 BitmapImage 以在屏幕上显示。然后我们需要将该数据作为字节数组保存到不同的存储机制,以便稍后可以重新加载它。as-is。

问题是据我了解,要将 BitmapImage 转换回字节数组,您必须使用编码器,但如果使用编码器,则 re-encoding每次保存时都会保存图像,因此除非您使用无损格式,否则您可能会降低图像质量,但是使用无损格式意味着您可能会加倍存储space 在诸如 jpeg 数据之类的东西上。

我的想法是创建一个 'holder' 类型来保存原始的原始数据(同样,从 jpg 文件中说)并用它来创建一个 BitmapImage on-demand,缓存它以供将来检索。

这是我目前对 'holder' 类型的了解。 (注意隐式转换器,所以我可以在接受 BitmapImage 的任何地方使用它):

#nullable enable

class PersistableBitmapImage{

    public PersistableBitmapImage(byte[] bytes)
        => _bytes = bytes;

    private byte[] _bytes;
    public byte[] Bytes {
        get => _bytes;
        set{
            _bytes = value;
            _bitmapImage = null;
        }
    }

    private BitmapImage? _bitmapImage;
    public BitmapImage BitmapImage
        => _bitmapImage ?? (_bitmapImage = Bytes.MakeBitmapImage());

    #region Implicit Operators

        public static implicit operator BitmapImage(PersistableBitmapImage persistableBitmapImage)
            => persistableBitmapImage.BitmapImage;

    #endregion Implicit Operators
}

这是创建 BitmapImage 的辅助扩展:

public static class ByteArrayExtensions {

    public static BitmapImage MakeBitmapImage(this byte[] bytes){

        using var ms = new MemoryStream(bytes);

        var bitmapImage = new BitmapImage();

        bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.StreamSource = ms;
        bitmapImage.EndInit();

        return bitmapImage;
    }
}

我在这个设计中遇到的问题是在访问 BitmapImage 的情况下,我现在将图像数据保存两次,一次是压缩格式(Data 属性) 和 realized/expanded BitmapImage 属性 中的另一个。唯一的 up-side 是后者只有 'realized' 如果访问并且压缩的前者是持久的。

对我来说还是有点'icky'的感觉。有没有办法不用两次保存图像数据就可以做到这一点?

您可以避免在此处处理 MemoryStream(处理 MemoryStream 虽然好的做法无论如何都不会做任何有趣的事情,但没有可处理的资源):

using var ms = new MemoryStream(bytes);

然后可以通过访问 bitmapImage.StreamSource 访问相同的内存流。请注意,返回的流 Position 可能不是 0,因为 BitmapImage 可能已经读取了它,所以不要忘记在必要时将位置重置为 0。通常调用 MemoryStream.GetBuffer() 是个坏主意(首选 ToArray()),但在这种特殊情况下,避免不必要的复制可能是合适的。