3

我有一个对应于我的屏幕分辨率(1280x800,每像素 24 位)的内存缓冲区,其中包含我的 24bpp 屏幕内容。我想将其转换为 8-bpp(即 Windows 中的半色调调色板)。我目前这样做: 1. 使用 CreateDIBSection 分配一个新的 1280x800 24-bpp 缓冲区并将其作为 DC 以及普通内存缓冲区访问 2. 使用 memcpy 从我的原始缓冲区复制到步骤 1 中的这个新缓冲区 3 . 使用 BitBlt 让 GDI 执行颜色转换

我想避免步骤 2 的额外 memcpy。为此,我可以想到两种方法:

一种。将我原来的 mem buf 包装在 DC 中以直接从中执行 BitBlt

湾。编写我自己的 24-bpp 到 8-bpp 颜色转换。我找不到有关 Windows 如何实现这种半色调颜色转换的任何信息。此外,即使我发现,我也不会使用 BitBlt 可以访问的 GDI 加速功能。

那么我该怎么做(a)或(b)?

谢谢!

4

4 回答 4

2

好的,解决问题的两个部分。

  1. 以下代码显示了如何获取位图中的像素,更改它们并将它们放回位图中。你总是可以生成一个正确大小和格式的虚拟位图,打开它,复制你的数据,然后你就有一个包含数据的位图对象:

    private void LockUnlockBitsExample(PaintEventArgs e)
    {
    
       // Create a new bitmap.
       Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
    
       // Lock the bitmap's bits.  
       Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
       System.Drawing.Imaging.BitmapData bmpData =
             bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
             bmp.PixelFormat);
    
       // Get the address of the first line.
       IntPtr ptr = bmpData.Scan0;
    
       // Declare an array to hold the bytes of the bitmap.
       int bytes  = bmpData.Stride * bmp.Height;
       byte[] rgbValues = new byte[bytes];
    
       // Copy the RGB values into the array.
       System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    
       // Set every third value to 255. A 24bpp bitmap will look red.  
       for (int counter = 2; counter < rgbValues.Length; counter += 3)
           rgbValues[counter] = 255;
    
       // Copy the RGB values back to the bitmap
       System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
    
       // Unlock the bits.
       bmp.UnlockBits(bmpData);
    
       // Draw the modified image.
       e.Graphics.DrawImage(bmp, 0, 150);
    }
    

要将内容转换为 8bpp,您需要使用 System.Drawing.Imaging.ColorMatrix 类。我手头没有半色调的正确矩阵值,但是这个例子的灰度和值的调整应该让您了解效果:

Graphics g = e.Graphics;
Bitmap bmp = new Bitmap("sample.jpg");
g.FillRectangle(Brushes.White, this.ClientRectangle);

// Create a color matrix
// The value 0.6 in row 4, column 4 specifies the alpha value
float[][] matrixItems = {
                            new float[] {1, 0, 0, 0, 0},
                            new float[] {0, 1, 0, 0, 0},
                            new float[] {0, 0, 1, 0, 0},
                            new float[] {0, 0, 0, 0.6f, 0}, 
                            new float[] {0, 0, 0, 0, 1}};
ColorMatrix colorMatrix = new ColorMatrix(matrixItems);

// Create an ImageAttributes object and set its color matrix
ImageAttributes imageAtt = new ImageAttributes();
imageAtt.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

// Now draw the semitransparent bitmap image.
g.DrawImage(bmp, this.ClientRectangle, 0.0f, 0.0f, bmp.Width, bmp.Height, 
            GraphicsUnit.Pixel, imageAtt);

imageAtt.Dispose();

稍后我将尝试使用半色调的矩阵值进行更新,其中可能有很多 0.5 或 0.333 值!

于 2008-09-18T10:50:36.557 回答
1

使用 CreateDIBitmap 而不是 CreateDIBSection。

于 2008-09-18T13:22:37.360 回答
1

如果您想消除副本(第 2 步),只需使用 CreateDIBSection 首先创建您的原始内存缓冲区。然后,您可以为该位图创建一个兼容的 DC,并将其用作 BitBlt 操作的源。

即,如果您首先使用 CreateDIBSection 位图而不是“普通内存”缓冲区,则无需在传输之前将内存从“普通内存”缓冲区复制到 CreateDIBSection 位图。

毕竟,使用 CreateDIBSection 分配的缓冲区本质上只是与您正在寻找的 CreateCompatibleDC 兼容的“普通内存”缓冲区。

于 2008-09-18T18:18:49.660 回答
0

首先,您是如何将屏幕内容放入这个 24bpp 内存缓冲区的?

避免不必要的 memcpy 的明显途径是通过首先创建 24bpp DIBSection 并将其作为目标缓冲区传递给 screengrab 函数来颠覆原始屏幕抓取。

如果那不可能,您仍然可以尝试通过创建一个描述内存缓冲区格式的 BITMAPINFOHEADER 来强制 GDI 进行硬提升,然后只需调用 StretchDIBits 将其 blit 到您的 8bpp DIBSection 上。

于 2008-10-13T15:51:32.430 回答