【问题标题】:Text erased from screenshot after using Clipboard.GetImage() on Windows 10?在 Windows 10 上使用 Clipboard.GetImage() 后从屏幕截图中删除的文本?
【发布时间】:2017-05-31 14:25:07
【问题描述】:

这是一个奇怪的问题: 我最近将我的工作站从 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 或直接从截图工具中保存):

现在这是我使用Clipboard.GetImage(); 导入屏幕截图后的样子。

如您所见,所有文本都被删除了,如果您仔细观察,您会发现通常为白色的背景现在是透明的。

如果我使用JpegBitmapEncoder 而不是PngBitmapEncoder,它可以正常工作,所以这应该是编码问题,但让我感到困惑的是:

  1. 这在 Windows 7 上从未发生过 - Windows 10 中发生了什么变化 这会让屏幕截图有什么不同?
  2. 如果我将屏幕截图保存到截图工具的文件中,则会创建一个 PNG(数据本身包含 PNG-Header)。那么为什么 PNG 不是正确的编码方式?

【问题讨论】:

  • 奇怪...据我所知,Windows 剪贴板甚至不支持透明度。
  • 在 Gimp 中粘贴肯定不行……更不用说,你截屏的窗口在那里没有透明度。
  • 如果您删除所有 alpha,图像仍然包含所有信息。我想我明白了,但我不是 100% 确定。您使用的是哪个版本的 .net 框架?因为在 3.5 上它似乎没有这样做,但 4+ 版本可能会有不同的反应。
  • 我还没有实际测试过差异,但我可以通过比较您的 4.5 体验和我的 3.5 体验来观察它。希望我的回答对你有用。

标签: c# image-processing windows-10 screenshot clipboard


【解决方案1】:

我一直在研究 windows 剪贴板,我想我知道发生了什么。请耐心等待,这是一个相当长的解释,并且需要大量研究才能获得有关 DIB 格式的足够信息,而这正是这个混乱的核心。

默认情况下,由于遗留原因,剪贴板根本不支持透明度;回到剪贴板最初设计的时候,还没有 alpha 通道这样的东西。但是,您可能知道,可以将多种格式放在剪贴板上,从而为读取剪贴板的程序提供更广泛的获取数据的可能性,并且似乎在最近的 Windows 版本中,图像显然也被放在了DIB(设备独立位图)格式的剪贴板。现在,.Net 框架 v3.5 通常从剪贴板中获取标准的非透明“图像”格式版本,因此它从不提供透明度,但似乎 4.5 版本实际上可能会得到这个 DIB 格式版本。

存在于剪贴板上的 DIB 在技术上是每像素 32 位的 RGB 图像。注意,RGB,不是 ARGB。这意味着每个像素有四个字节,但是虽然红色、绿色和蓝色字节是正常填充的,但第四个字节实际上是未定义的,不应指望它实际上表示“alpha”。它只是为了让数据与四个字节的倍数很好地对齐。

这个 DIB 的内部格式设置为 32 位“BITFIELDS”,与标准的 32 位“RGB”类型不同,它在标头中明确定义了每个 32 位像素的哪些位用于每种颜色。但它只定义了三个这样的位域;为红色、绿色和蓝色。没有第四个字段;它不应该有阿尔法。考虑一下,如果该图像是由一个系统创建的,该系统确实只是将其视为没有 alpha 的 RGB,并且不会事先清除其内存(因为许多 C/C++ 系统没有),并且实际上只写那些R、G 和 B 字节,那么第四个字节可能只是先前操作在内存中留下的随机垃圾。您甚至无法确定它是否被清除为值 0 或设置为标准不透明值 255。

还有一个问题......因为很多应用程序,显然包括 Windows 10 和 .Net framework 4.5,确实这样对待它。当我在由 2 个不同高度的屏幕组成的 Windows 10 桌面上按下 Print Screen 时,生成的图像实际上是由 Windows 本身作为具有透明度的混蛋-BITFIELDS-DIB 格式放入剪贴板的;当我使用我的函数转储图像以将该 DIB 保存为 ARGB 图像时,图像上落在我实际显示器之外的区域确实是透明,而不仅仅是黑色;它是图像中唯一将“alpha”字节设置为 0 的部分。所以这似乎表明 Windows 10 本身也认为格式支持 alpha。

所以我们这里的问题似乎是微软本身没有正确使用自己的规范。 (如果您想知道,是的,他们制定了 DIB 规范。)他们完全可以切换到具有更大标头的更新的 DIB 格式,确实支持真正的 alpha,但是他们使用了一个混蛋的旧 RGB 格式,显然每个人(我在从 Google Chrome 复制图像时发现它)只是假设包含 alpha。

反过来,这似乎会导致像你一样的奇怪行为。我怀疑资源管理器窗口的绘图机制中的某些东西用某些东西填充了这些 alpha 字节,甚至可能是它自己的绘图层之一的 alpha,并且在将最终图像放在剪贴板上时它永远不会被清除。然后,正如您所注意到的,.Net 框架 4.5 显然假定这些额外的字节确实是您获得的图像的 alpha 通道。

解决方案是将这些字节清除为 255,或者让您的系统将结果解释为 RGB 而不是 ARGB。我实际上可以帮助您的第二个:我详细说明了如何在GetImageData 函数in this answer 中从图像数据中获取字节,this one 有我的BuildImage 方法用于将字节写回新图像。因此,如果您只是使用它来获取 32 位 ARGB 数据,然后将其写回新的 32 位 RGB 图像,则可以使框架将图像数据视为不透明,而无需修改单个字节。

【讨论】:

  • 注意,不是每个人都假定它包含 alpha。我在 Gimp 问题跟踪器上找到了一个特定的票证,由于不可靠的原因,该票证拒绝该格式为 alpha-capable。 Gimp 将始终将此 DIB 视为不透明的。
  • 如果您对我的完整剪贴板复制代码感兴趣,我将其发布在这里:stackoverflow.com/a/46424800/395685
  • 我有一个程序可以截取控制元素的屏幕截图并将它们放入剪贴板。为了不重复截图到填满剪贴板的地步,我尝试确定剪贴板图像是否已经存在,我不得不使用以下代码来处理图像字节序列:if ((i - 1) % 4 == 0) ->bytes[i] = 0;,我碰巧看到了你今天的研究,所以我知道原理:-)
  • @CodingNinja 除非您实际上正在处理所有字节,否则仅在第一个 alpha 字节偏移量上使 for 循环开始 i 并以 4 步增加它会更有效,尽管。循环变小了 4 倍,并且不需要 if 检查。
猜你喜欢
  • 2021-03-20
  • 2018-05-27
  • 2017-09-17
  • 2017-09-21
  • 2016-05-31
  • 2016-08-27
  • 2020-05-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多