C#捕获屏幕为8位(256色)位图

时间:2012-04-17 04:37:15

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

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

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 ++编写的程序,我正在尝试memic。在这种情况下,设置VCL的TBitmap对象的像素格式很简单。我注意到Bitmap.PixelFormat在.NET中是只读的,呃。

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

2 个答案:

答案 0 :(得分:6)

将全色位图转换为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);
    }

答案 1 :(得分:1)

使用常规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);
    }
}