【问题标题】:C# Capture screen to 8-bit (256 color) bitmapC# 将屏幕捕获到 8 位(256 色)位图
【发布时间】:2012-04-28 10:22:00
【问题描述】:

我正在使用此代码来捕获屏幕:

public Bitmap CaptureWindow(IntPtr handle)
{
    // get te hDC of the target window
    IntPtr hdcSrc = User32.GetWindowDC(handle);
    // get the size
    User32.RECT windowRect = new User32.RECT();
    User32.GetWindowRect(handle, ref windowRect);
    int width = windowRect.right - windowRect.left;
    int height = windowRect.bottom - windowRect.top;
    // create a device context we can copy to
    IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
    // create a bitmap we can copy it to,
    // using GetDeviceCaps to get the width/height
    IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
    // select the bitmap object
    IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
    // bitblt over
    GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
    // restore selection
    GDI32.SelectObject(hdcDest, hOld);
    // clean up 
    GDI32.DeleteDC(hdcDest);
    User32.ReleaseDC(handle, hdcSrc);
    // get a .NET image object for it
    Bitmap img = Image.FromHbitmap(hBitmap);
    // free up the Bitmap object
    GDI32.DeleteObject(hBitmap);
    return img;
}

然后我想将位图转换为 256 色(8 位)。我尝试了这段代码,但收到一个错误,提示无法从索引位图格式创建图像:

Bitmap img8bit = new Bitmap(img.Width,img.Height,
                           System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
Graphics g = Graphics.FromImage(img8bit);
g.DrawImage(img,new Point(0,0));

我确实看到了一些在不同格式之间转换位图的示例,但就我而言,我正在寻找从屏幕捕获时执行此操作的最佳方法。例如,如果有一种方法可以通过创建一个 8 位位图来更好地工作,然后将屏幕blit 到那个位置,那将比先将屏幕捕获到可兼容位图然后转换它更可取。除非最好先捕获然后再转换。

我有一个使用 Borland Builder 6.0 VCL 用 C++ 编写的程序,我正在尝试模仿它。在这种情况下,为 VCL 的 TBitmap 对象设置像素格式很简单。我注意到 Bitmap.PixelFormat 在 .NET 中是只读的,呃。

更新:就我而言,我认为答案不像其他需要找出最佳调色板条目的用法那样复杂,因为使用屏幕 DC 的 Graphics.GetHalftonePalette 应该没问题,因为我的原始位图来自屏幕,而不仅仅是来自文件/电子邮件/下载/等的任何随机位图。我相信有些事情可以用涉及 DIB 和 GetHalftonePalette 的 20 行代码来完成——只是还找不到。

【问题讨论】:

  • 您是否总是要捕获整个屏幕?如果是这样,有一个内置的方法可以使用 .NET,Graphics.CopyFromScreen
  • PixelFormat 属性是只读的,但是有一个位图构造函数可以让您指定像素格式。见msdn.microsoft.com/en-us/library/3z132tat.aspx
  • @eselk 你有没有进一步了解这个? :)

标签: c# .net graphics bitmap screen-capture


【解决方案1】:

将全彩色位图转换为 8bpp 是一项困难的操作。它需要创建图像中所有颜色的直方图,并创建一个调色板,其中包含一组优化的颜色,这些颜色最好映射到原始颜色。然后使用抖动或误差扩散等技术来替换颜色与调色板不完全匹配的像素。

这最好留给专业的图形库,比如 ImageTools。在 .NET 框架中有一种廉价的方法可以被欺骗。您可以使用 GIF 编码器,这是一种具有 256 色的文件格式。结果不是最好的,它使用抖动,有时很明显。再说一次,如果你真的关心图像质量,那么无论如何你都不会使用 8bpp。

    public static Bitmap ConvertTo8bpp(Image img) {
        var ms = new System.IO.MemoryStream();   // Don't use using!!!
        img.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
        ms.Position = 0;
        return new Bitmap(ms);
    }

【讨论】:

  • 谢谢。似乎在 Windows API 级别有一些方法可以在不同像素格式的设备上下文之间进行 bitblt(),并且操作系统会为您处理这些细节。不过我可能是错的,如果没有人按照这些思路发布任何答案,我会接受这个答案。质量显然不是一个大问题,但无论 VCL 使用什么方法都会产生不错的结果,尤其是在阅读文本时。我可能需要查看他们的源代码以了解他们做了什么,然后尝试转换为 .NET 或 pinvokes。
  • 已接受。查看 VCL 源代码后,我确实看到它正在做很多工作。我认为 Win32 API 会完成大部分工作,但我想不会。从 VCL 之类的遗留库到 .NET 很糟糕,现在我必须为以前简单的事情做更多的工作,但这就是生活。 GIF 对我不起作用,因为抖动很糟糕,像 VCL 那样转换为 256 色实际上对于大多数商业应用程序/基本屏幕来说非常好(对于照片或游戏屏幕截图来说不是那么多)。
  • 我确实觉得有趣的是,VCL 方法产生的结果与制作屏幕截图、粘贴到 MSPaint、另存为 256 色位图到文件中的结果相同......但我想他们可能只是在使用相同的算法。
  • +1,但我可以问一下,为什么不使用using?我正在使用using,一切都很好。
【解决方案2】:

使用常规 PixelFormat 捕获屏幕,然后使用 Bitmap.Clone() 将其转换为优化的 256 索引颜色,如下所示:

public static Bitmap CaptureScreen256()
{
    Rectangle bounds = SystemInformation.VirtualScreen;

    using (Bitmap Temp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format24bppRgb))
    {
        using (Graphics g = Graphics.FromImage(Temp))
        {
            g.CopyFromScreen(0, 0, 0, 0, Temp.Size);
        }

        return Temp.Clone(new Rectangle(0, 0, bounds.Width, bounds.Height), PixelFormat.Format8bppIndexed);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    • 1970-01-01
    • 1970-01-01
    • 2013-05-20
    • 2012-03-26
    • 2011-12-20
    相关资源
    最近更新 更多