使用索引颜色表捕获程序的屏幕截图
网上有很多关于如何使用 C# 捕获和保存屏幕截图的教程。 例如,我使用 此网站 来获取我的解决方案:
using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(screenshot))
{
g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
screenshot.Save("screenshot.png", ImageFormat.Png);
}
效果很好在大多数程序中,但我想要捕获的程序使用 8 位索引颜色表。使用此代码拍摄的该程序的屏幕截图很奇怪。我的问题是,是否有人可以为我指明正确的方向,以捕获 C# 中带有索引颜色表的程序的屏幕截图?
为了帮助您帮助我,我将在下面描述我的发现和尝试的解决方案。
使用 8 位索引颜色表的全屏程序代码捕获的屏幕截图大部分是黑色(约 88%),其中只有 17 种其他颜色。我没有看到这 17 种颜色中的图案。在程序本身中几乎使用了所有 256 种颜色。我希望在黑色屏幕截图中也能找到 256 种颜色,这可以表明简单的一对一关系,但事实并非如此。
我还想指出,手动截取的屏幕截图是完美的(例如,当我将它们粘贴到 MS Paint 中时)。因此,我尝试在手动截取屏幕截图后从 System.Windows.Forms.Clipboard.GetImage() 获取图像,但这会返回一个 COMobject,我不知道该如何处理。当然,它必须包含有效屏幕截图的信息,因为 MS Paint 知道如何从该 COM 对象中提取它。我怎样才能自己提取它,最好是用 C# 提取?
但我的主要问题是:有人能给我指出正确的方向,用 C# 捕获带有索引颜色表的程序的屏幕截图吗?
There are numerous tutorial on the web on how to capture and save a screenshot using C#.
For example, I used this website to obtain my solution:
using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(screenshot))
{
g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
screenshot.Save("screenshot.png", ImageFormat.Png);
}
It works fine in most programs, but the program I want to capture uses an 8 bits indexed color table. Screenshots of that program taken with this code are strange. My question is if someone can point me in the right direction for capturing screenshots of programs with indexed color tables in C#?
To help you help me, I will describe my findings and attempted solutions below.
The screenshots captured with this code of a full screen program using an 8 bits indexed color table is mostly black(for ~88 %) and there are only 17 other colors in it. I don't see a pattern in those 17 colors. In the program itself almost all 256 colors are used. I was hoping to find 256 colors in the black screenshots as well, which could indicate a simple one-on-one relationship, but that is not the case.
I would also like to note that screenshots taken manually are perfect(when I paste them in MS Paint for example). For that reason I tried fetching the image from System.Windows.Forms.Clipboard.GetImage() after taking a screenshot manually, but that returns a COMobject and I wouldn't know what to do with that. Surely that must contain the information of a valid screenshot, since MS Paint knows how to extract it from that COM object. How can I extract that myself, preferably in C#?
But my main question: Can someone point me in the right direction for capturing screenshots of programs with indexed color tables with C#?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果我理解正确,这里的问题是您正在复制像素值(调色板索引)而不是调色板本身。我没有找到用纯 C# 复制调色板的方法,但使用 P/Invoke 和 DirectX 的替代方案...因为两者都增加了对 Windows 的依赖,所以我选择了 P/Invoke,因为它更容易并且不会依赖于 DirectX。
我有两种方法可以为您提供...
第一种方法:
此方法取决于
GetHalftonePalette
为您提供正确的调色板。我从未使用调色板进行过测试(我太年轻了)...所以我决定编写第二种方法,因为窗口应该在某些条件下自动处理调色板。注意:不确定将对 CopyFromScreen 的调用移至 using 块的开头是否更好,或者忽略对 RealizePalette 的调用(我太年轻了)。第二种方法:
这两种方法都在正常条件下有效。请进行测试,您可能需要执行第二种方法,并添加复制原始调色板(即......当 Windows 不自动处理它们时),但我不确定如何这样做(我太年轻)并希望这个方法有效。
最后,如果您要承担额外的依赖项并希望它与 Windows 不同,也许 GTK#(Windows 中有一个可用版本)是一个不错的选择。
请参阅如何使用 Mono C# 截取屏幕截图?它解决了类似的问题,并提供了 GTK# 的示例代码。
注意:
下面的代码在这里似乎运行良好,您是否通过此代码得到任何异常?
If I understand correctly, the problem here is that you are copying the pixel values (the palette indexes) but not the palette itself. I didn't find a way to copy the palette with pure C#, but with P/Invoke and an alternative with DirectX... since both adds a dependency to Windows i've opted for P/Invoke as it's easier and doesn't depend on DirectX.
I have two methods to offer you...
The first Method:
This method depends on
GetHalftonePalette
giving you the right palette. I have never tested with palettes (I'm too young)... so I decided to write a second method based in that windows should handle palettes automatically in some conditions. Note: not sure if moving the call to CopyFromScreen to the begin of the using block is better, or ignoring the call to RealizePalette (I'm too young).The second Method:
Both methods works on normal conditions. Please do your testing, it may be that you will need to do the second method with the addition of duplicating the original palette (that is... when windows doesn't handle them automatically), but I'm not sure on how to do that (I'm too young) and hope this method works.
Lastly, if you are to afford an extra dependency and want it to be different from Windows, maybe GTK# (there is a version available form Windows) is a good option.
Please see How to take a screenshot with Mono C#? as it addresses a similar problem, and provides example code for GTK#.
Note:
The following code seems to work well here, do you get any exceptions by this code?
事情不是这样的。您正在捕获屏幕,而不是直接捕获程序的输出。视频适配器的设置很重要,对于任何最新的机器来说,它都是 32bpp。老的可能是16bpp。不支持尝试将此类非索引像素格式复制到带有调色板的位图中。创建提供最佳色彩保真度的调色板的算法在计算上非常重要。
不用担心,32bpp 图像将与程序的输出无法区分。如果压缩文件确实很重要,请将其存储为 .gif。它看起来不太好,GIF 编码器使用抖动来压缩颜色表。
That's not how it works. You are capturing the screen, not directly the output of the program. The setting of the video adapter matters, it will be 32bpp for any recent machine. Maybe 16bpp for old ones. Trying to copy such a non-indexed pixel format into a bitmap with a palette isn't supported. The algorithm to create a palette that provides the best color fidelity is computationally quite non-trivial.
Just don't bother, the 32bpp image will be indistinguishable from the program's output. If squeezing the file is really important then store it as a .gif. It isn't going to look great, the GIF encoder uses dithering to compress the color table.
建议的解决方案不起作用,我最近才解决了这个问题。
我通过向 Windows 发送 PrintScreen 按键按下和释放来截取屏幕截图,并以 MemoryStream 的形式从剪贴板获取数据,并弄清楚如何在该流中对屏幕截图进行解码并将其转换为位图。不是很吸引人,但至少它有效......
The suggested solutions did not work and I only recently solved this problem.
I took a screenshot by sending a PrintScreen key press and release to windows, and acquired the data from the clipboard in the form of a
MemoryStream
and figured out how the screenshot was decoded in that stream and converted that to a bitmap. Not very appealing, but at least it works...