2012-04-17 94 views
2

我使用这个代码来捕捉屏幕:C#捕获屏幕为8位(256色)位图

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位位图,然后将光标移动到该位置,则优先于Caputring屏幕首先对位图进行兼容,然后对其进行转换。除非最好捕捉然后转换。

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

更新:在我来说,我不认为答案是复杂的,需要找出最佳的调色板项,因为Graphics.GetHalftonePalette使用屏幕一些其他使用DC应该是很好的,因为我原来的位图来自屏幕,而不仅仅是任何可能来自文件/电子邮件/下载/等的随机位图。我相信有20行代码涉及到DIB和GetHalftonePalette可以完成,但目前还找不到它。

+3

你总是会被捕捉整个屏幕?如果是这样,那么有一种内置的方法可以用.NET来实现这一点,即'Graphics.CopyFromScreen'。 – Ryan 2012-04-17 04:41:17

+0

'PixelFormat'属性是只读的,但是有一个Bitmap构造函数可以让你指定像素格式。请参阅http://msdn.microsoft.com/en-us/library/3z132tat.aspx。 – 2012-04-17 04:45:44

+0

@eselk你有没有进一步与此? :) – 2013-04-09 22:34:39

回答

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); 
    } 
+0

谢谢。看起来在Windows API级别上,有不同像素格式的设备上下文之间的bitblt()方法,并且操作系统为您处理这些细节。我可能是错误的,如果没有人发布任何答案,我会接受这个答案。质量并不是一个很大的问题,但无论VCL使用什么方法,都会产生不错的结果,特别是在阅读文本时。我可能不得不看他们的源代码,看看他们做了什么,然后尝试翻译成.NET或pinvokes。 – eselk 2012-04-17 15:31:06

+0

已接受。看完VCL源代码后,我发现它正在做很多工作。我假定Win32 API将完成大部分工作,但我猜不。吮吸从像VCL这样的传统库到。NET,现在我必须为之前很简单的事情做更多的工作,但那就是生活。 GIF不适合我,因为抖动非常糟糕,以VCL的方式转换为256色,实际上它们非常适合寻找大多数业务应用程序/基本屏幕(不是用于照片或游戏屏幕截图)。 – eselk 2012-04-17 22:56:10

+0

我的确发现有趣的是,VCL方法产生相同的结果做屏幕上限,粘贴在MSPaint中,保存为256色的位图文件...但我想他们可能只是使用相同的算法。 – eselk 2012-04-17 23:01:40

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); 
    } 
}