Как обернуть существующий буфер памяти как DC для GDI - PullRequest
3 голосов
/ 18 сентября 2008

У меня есть буфер памяти, соответствующий моему разрешению экрана (1280x800 при 24 битах на пиксель), который содержит содержимое моего экрана в 24bpp. Я хочу преобразовать это в 8-bpp (т.е. полутоновую цветовую палитру в Windows). В настоящее время я делаю это: 1. Используйте CreateDIBSection для выделения нового буфера 1280x800 с 24 битами на пиксель и доступа к нему как к DC, так и к обычному буферу памяти 2. Используйте memcpy для копирования из моего исходного буфера в этот новый буфер с шага 1 3. Используйте BitBlt, чтобы позволить GDI выполнить преобразование цвета

Я хочу избежать дополнительной памяти в шаге 2. Для этого я могу придумать два подхода:

а. Оберните мой оригинальный mem buf в DC, чтобы выполнить BitBlt прямо с него

б. Напишите мое собственное преобразование цветов 24-bpp в 8-bpp. Я не могу найти никакой информации о том, как Windows реализует это преобразование цветов полутонов. Кроме того, даже если я узнаю, я не буду использовать ускоренные функции GDI, к которым у BitBlt есть доступ.

Так как же мне сделать (а) или (б)?

спасибо!

Ответы [ 4 ]

2 голосов
/ 18 сентября 2008

ОК, чтобы решить две части проблемы.

  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!

1 голос
/ 18 сентября 2008

Используйте CreateDIBitmap вместо CreateDIBSection.

0 голосов
/ 13 октября 2008

Как вы в первую очередь поместили содержимое экрана в этот буфер памяти 24bpp?

Очевидный способ избежать ненужного memcpy - это подорвать оригинальный скриншот, сначала создав DIBSection 24bpp, и передав его в функцию screengrab в качестве буфера назначения.

Если это невозможно, вы все равно можете попытаться заставить GDI выполнять тяжелую работу, создав BITMAPINFOHEADER, описывающий формат буфера памяти, и просто вызвать StretchDIBits, чтобы скопировать его на вашу DIBSection 8bpp.

0 голосов
/ 18 сентября 2008

Если вы хотите удалить копию (шаг 2), просто используйте CreateDIBSection, чтобы сначала создать свой оригинальный буфер памяти. Затем вы можете просто создать совместимый DC для этого растрового изображения и использовать его в качестве источника для операции BitBlt.

т.е. нет необходимости копировать память из буфера «обычная память» в битовую карту CreateDIBSection перед копированием, если вы сначала используете битовую карту CreateDIBSection вместо буфера «простой памяти».

В конце концов, буфер, выделенный с помощью CreateDIBSection, по сути, является просто буфером «простой памяти», который совместим с CreateCompatibleDC, что вы и ищете.

...