Подтвердить что ты не робот

Как сохранить изображение в оригинальном формате?

Как сохранить изображение с его исходной кодировкой?

Кажется, что единственный способ сохранить изображение - использовать 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);
}

но проблема в том, что

  • ImageSource не всегда является файлом BitmapImage (Clipboard.GetImage()) и
  • image.StreamSource может быть пустым в некоторых случаях (кажется, когда изображение загружается через относительный Uri)

Любые предложения?

4b9b3361

Ответ 1

Основной вопрос здесь: "Как взять произвольный ImageSource и узнать, в каком формате он был первоначально закодирован?"

Ответ заключается в том, что вы не всегда можете это сделать, но для большинства практических целей существует обходное решение. Как показывает ваш код выше, как только вы узнали, какой формат использовать остальные, легко.

Причина, по которой вы не всегда можете узнать исходный формат, заключается в том, что возможно (с некоторым очень сложным кодом!) подклассировать BitmapSource для создания нового ImageSource, который получает свои данные из любого места, где захотите. Например, я мог бы реализовать PseudoRandomBitmapSource, который возвращает случайные пиксели. В этом случае "исходный формат", вероятно, является семенем, используемым для генератора случайных чисел.

Если вы имеете дело с одним из встроенных классов ImageSource, способ узнать исходную кодировку зависит от того, какой именно класс вы используете:

  • Для BitmapImage вы можете использовать StreamSource или UriSource, независимо от того, какой из них установлен. Любой из них может быть передан в BitmapDecoder.Create overload. В вашем примере кода показано, как это сделать для StreamSource. Это точно так же для UriSource, за исключением того, что вам нужно новое Uri(BaseUri, UriSource) и передать это BitmapDecoder.Create перегрузка, которая принимает Uri.

  • Для ColorConvertedBitmap, CroppedBitmap, FormatConvertedBitmap и TransformedBitmap существует общедоступное свойство "Источник", которое вы можете использовать для получения исходного источника, а затем рекурсивно проверяйте его кодировку с помощью этого алгоритма.

  • Для CachedBitmap вы можете перейти только к исходному растровому изображению через внутреннее поле. Вы можете получить доступ, используя отражение, если у вас есть достаточно привилегий, иначе вам не повезло.

  • Для RenderTargetBitmap, WritableBitmap, D3DImage и DrawingImage нет оригинальной кодировки, поскольку изображение создается "на лету" из векторного формата или алгоритма.

  • Для BitmapFrame используйте свойство Decoder для получения декодера, затем используйте CodecInfo.ContainerFormat.

  • Для InteropBitmap или UnmanagedBitmapWrapper это очень сложно. В основном вам нужно использовать отражение, чтобы прочитать внутреннее свойство WicSourceHandle, а затем вызвать DangerousGetHandle(), чтобы получить IntPtr, который на самом деле является неуправляемым IUnkown. Использование неуправляемого кода, QueryInterface для IWICBitmapDecoder. В случае успеха вы можете вызвать 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" без каких-либо других, вы знаете, что исходный формат был потерян.