Текст, удаленный со скриншота после использования Clipboard.GetImage() в Windows 10?

Это странно: я недавно обновил свою рабочую станцию ​​с Windows 7 до Windows 10. У меня есть клиент чата, который принимает изображения из буфера обмена, используя код ниже:

if (Clipboard.ContainsImage())
{
        BitmapSource source = Clipboard.GetImage();
        BitmapFrame frame = BitmapFrame.Create(source);
        var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
        encoder.Frames.Add(frame);
        var stream = new MemoryStream();
        encoder.Save(stream);
        byte[] daten = stream.ToArray();
        if (daten != null && daten.Length > 0)
        {
            sendFile(DateTime.Now.ToString("yyyyMMddHHmmss_") + "clipboard.png", stream.ToArray());
        }
}

Вот как должна выглядеть область моего снимка экрана (например, если я вставлю ее в MS-Paint или сохраню прямо из Snipping Tool): Фактический скриншот

Теперь вот как это выглядит после импорта скриншота с помощью Clipboard.GetImage();, Снимок экрана после использования Glipboard.GetImage

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

Если я использую JpegBitmapEncoder вместо PngBitmapEncoder, он работает нормально, так что это, собственно, проблема с кодировкой, но вот что меня удивляет:

  1. Это никогда не происходило в Windows 7 - что изменилось в Windows 10, что могло сделать скриншоты другими?
  2. Если я сохраню скриншот в файл из Snipping Tool, создается PNG (с PNG-заголовком в самих данных). Так почему же PNG не подходит для кодирования?

1 ответ

Решение

Я исследовал буфер обмена Windows и думаю, что знаю, что происходит. Имейте в виду, это довольно длинное объяснение, и потребовалось много исследований, чтобы получить достаточно информации о формате DIB, который лежит в основе этого беспорядка.

По умолчанию буфер обмена вообще не поддерживает прозрачность по устаревшим причинам; назад, когда буфер обмена был впервые разработан, не было такого понятия, как альфа-каналы. Как вы, возможно, знаете, однако, в буфер обмена могут быть помещены несколько форматов, чтобы дать программам, читающим буфер обмена, более широкий спектр возможностей для извлечения данных, и кажется, что в последних версиях Windows изображение, по-видимому, также помещается в буфер обмена. буфер обмена в формате DIB (Device Independent Bitmap). Теперь.Net Framework v3.5 обычно берет из буфера обмена стандартную непрозрачную версию формата "Изображение", поэтому она никогда не дает прозрачности, но кажется, что 4.5 действительно может получить эту DIB вместо этого.

DIB, как он существует в буфере обмена, технически представляет собой RGB-изображение размером 32 бита на пиксель. Обратите внимание, RGB, а не ARGB. Это означает, что на каждый пиксель приходится четыре байта, но хотя красный, зеленый и синий байты заполнены нормально, четвертый байт фактически не определен и не должен рассчитывать на фактическое значение "альфа". Это просто для того, чтобы данные были выровнены с точностью до четырех байтов.

Внутренний формат этого DIB установлен на 32-битный "BITFIELDS", который отличается от стандартного 32-битного типа "RGB" тем, что он конкретно определяет в заголовке, какие биты каждого 32-битного пикселя использовать для каждого цвета. Но он определяет только три таких битовых поля; для красного, зеленого и синего. Четвертого поля нет; это не должно иметь альфа. Подумайте, если этот образ был создан системой, которая действительно обрабатывает его как RGB без альфа, и который заранее не очищает свою память (как это делают многие системы C/C++), и который на самом деле записывает только те R,G и B байтов, тогда четвертый байт может быть просто случайным мусором, оставленным в памяти от предыдущих операций. Вы даже не можете быть уверены, что оно очищено до значения 0 или установлено в стандартное непрозрачное значение 255.

И есть проблема... потому что многие приложения, в том числе, по-видимому, Windows 10 и.Net Framework 4.5, так и поступают. Когда я нажал "Печать экрана" на рабочем столе Windows 10, состоящем из 2 экранов разной высоты, полученное изображение фактически было помещено в буфер обмена самой Windows как формат bastard-BITFIELDS-DIB с прозрачностью в нем; когда я выгружал изображение с помощью своей функции, чтобы сохранить этот DIB как ARGB-изображение, область на изображении, которая выходила за пределы имеющихся у меня мониторов, была действительно прозрачной, а не только черной; это был единственный фрагмент изображения с его альфа-байтами, установленными в 0. Так что, похоже, это указывает на то, что сама Windows 10 также считает формат альфа-совместимым.

Таким образом, проблема в том, что сама Microsoft неправильно использует собственные спецификации. (Если вам интересно, да, они сделали спецификации DIB.) Они могли бы идеально переключиться на более свежий формат DIB с большим заголовком, который поддерживает настоящую альфу, но вместо этого они используют убогий старый формат RGB, который, по-видимому, каждый (я обнаружил это при копировании изображения из Google Chrome) просто предполагает, что содержит альфа.

Это, в свою очередь, кажется, приводит к странному поведению, подобному тому, что вы имели. Я подозреваю, что что-то в механике рисования окна Explorer заполняет эти альфа-байты чем-то, может быть, даже альфа одного из его собственных слоев рисования, и это никогда не очищается при помещении окончательного изображения в буфер обмена. И затем, как вы заметили,.Net Framework 4.5, очевидно, предполагает, что эти дополнительные байты действительно являются альфа-каналом для изображения, которое вы получаете.

Решение состоит в том, чтобы либо очистить эти байты до 255, либо заставить вашу систему интерпретировать результат как RGB вместо ARGB. Второй из них, с которым я могу вам помочь: я подробно описал, как получить байты из данных изображения в GetImageData функция в этом ответе, и этот имеет мой BuildImage метод записи байтов обратно в новое изображение. Поэтому, если вы просто используете это для получения 32-битных данных ARGB, а затем просто записываете их обратно в новое 32-битное RGB-изображение, вы можете заставить каркас обрабатывать данные изображения как непрозрачные без необходимости изменения одного байта.

Другие вопросы по тегам