Фундаментальный вопрос здесь: «Как взять произвольный ImageSource и узнать, в каком формате он был изначально закодирован?»
Ответ в том, что вы не всегда можете сделать это, но для большинства практических целей есть обходной путь. Как показывает приведенный выше код, после того, как вы узнали, какой формат использовать, все просто.
Причина, по которой вы не всегда можете узнать исходный формат, заключается в том, что возможно (с некоторым очень сложным кодом!) Создать подкласс BitmapSource, чтобы создать новый ImageSource, который получает свои данные из любого места, которое вам нравится. Например, я мог бы реализовать PseudoRandomBitmapSource, который возвращает случайные пиксели. В этом случае «исходный формат», вероятно, является начальным числом, используемым для генератора случайных чисел.
Если вы имеете дело с одним из встроенных классов ImageSource, способ узнать оригинальную кодировку зависит от того, какой именно класс вы используете:
Для BitmapImage вы можете использовать StreamSource или UriSource, в зависимости от того, какой из них установлен. Любой из них может быть передан перегрузке BitmapDecoder.Create. В вашем примере кода показано, как это сделать для StreamSource. Это то же самое для UriSource, за исключением того, что вам нужно новое Uri(BaseUri, UriSource)
и передать его перегрузке BitmapDecoder.Create, которая принимает Uri.
Для ColorConvertedBitmap, CroppedBitmap, FormatConvertedBitmap и TransformedBitmap есть открытое свойство «Source», которое можно использовать для получения базового источника, а затем рекурсивно проверить его кодировку с помощью этого алгоритма.
Для 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" без чего-либо еще вы знаете, что оригинальный формат был потерян.