如何将现有内存缓冲区包装为 GDI 的 DC

发布于 2024-07-04 12:33:45 字数 500 浏览 5 评论 0原文

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

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

: 将我原来的 mem buf 包装在 DC 中,以便直接从中执行 BitBlt

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

那么我该如何做(a)或(b)呢?

谢谢!

I have a memory buffer corresponding to my screen resolution (1280x800 at 24-bits-per-pixel) that contains my screen contents at 24bpp. I want to convert this to 8-bpp (ie. Halftone color palette in Windows).
I currently do this:
1. Use CreateDIBSection to allocate a new 1280x800 24-bpp buffer and access it as a DC, as well as a plain memory buffer
2. Use memcpy to copy from my original buffer to this new buffer from step 1
3. Use BitBlt to let GDI perform the color conversion

I want to avoid the extra memcpy of step 2. To do this, I can think of two approaches:

a. Wrap my original mem buf in a DC to perform BitBlt directly from it

b. Write my own 24-bpp to 8-bpp color conversion. I can't find any info on how Windows implements this halftone color conversion. Besides even if I find out, I won't be using the accelerated features of GDI that BitBlt has access to.

So how do I do either (a) or (b)?

thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心作怪 2024-07-11 12:33:45

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

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

    private void LockUnlockBitsExample(PaintEventArgs e) 
      { 
    
         // 创建一个新的位图。 
         位图 bmp = new Bitmap("c:\\fakePhoto.jpg"); 
    
         // 锁定位图的位。   
         矩形 rect = new 矩形(0, 0, bmp.Width, bmp.Height); 
         System.Drawing.Imaging.BitmapData bmpData = 
               bmp.LockBits(矩形,System.Drawing.Imaging.ImageLockMode.ReadWrite, 
               bmp.PixelFormat); 
    
         // 获取第一行的地址。 
         IntPtr ptr = bmpData.Scan0; 
    
         // 声明一个数组来保存位图的字节。 
         int 字节 = bmpData.Stride * bmp.Height; 
         byte[] rgbValues = 新字节[字节]; 
    
         // 将 RGB 值复制到数组中。 
         System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, 字节); 
    
         // 将每隔三个值设置为 255。24bpp 位图将显示为红色。   
         for (int counter = 2; counter < rgbValues.Length; counter += 3) 
             rgbValues[计数器] = 255; 
    
         // 将 RGB 值复制回位图 
         System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, 字节); 
    
         // 解锁位。 
         bmp.UnlockBits(bmpData); 
    
         // 绘制修改后的图像。 
         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 值!

OK, to address the two parts of the problem.

  1. the following code shows how to get at the pixels inside of a bitmap, change them and put them back into the bitmap. You could always generate a dummy bitmap of the correct size and format, open it up, copy over your data and you then have a bitmap object with your data:

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

To convert the contents to 8bpp you'll want to use the System.Drawing.Imaging.ColorMatrix class. I don't have at hand the correct matrix values for half-tone, but this example grayscales and adjustment of the values should give you an idea of the effect:

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

I shall try and update later with the matrix values for half-tone, it's likely to be lots 0.5 or 0.333 values in there!

丘比特射中我 2024-07-11 12:33:45

使用 CreateDIBitmap 而不是 CreateDIBSection。

Use CreateDIBitmap rather than CreateDIBSection.

物价感观 2024-07-11 12:33:45

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

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

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

If you want to eliminate the copy (step 2), just use CreateDIBSection to create your original memory buffer in the first place. Then you can just create a compatible DC for that bitmap and use it as the source for the BitBlt operation.

I.e. there is no need to copy the memory from a "plain memory" buffer to a CreateDIBSection bitmap prior to blitting if you use a CreateDIBSection bitmap instead of a "plain memory" buffer in the first place.

After all, a buffer allocated using CreateDIBSection is essentially just a "plain memory" buffer that is compatible with CreateCompatibleDC, which is what you are looking for.

贱人配狗天长地久 2024-07-11 12:33:45

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

避免不必要的 memcpy 的明显途径是首先创建 24bpp DIBSection,然后将其作为目标缓冲区传递给 screengrab 函数,从而破坏原始屏幕抓取。

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

How did you get the screen contents into this 24bpp memory buffer in the first place?

The obvious route to avoiding a needless memcpy is to subvert the original screengrab by creating the 24bpp DIBSection first, and passing it to the screengrab function as the destination buffer.

If thats not possible, you can still try and coerce GDI into doing the hard lifting by creating a BITMAPINFOHEADER describing the format of the memory buffer, and just call StretchDIBits to blit it onto your 8bpp DIBSection.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文