Как обернуть существующий буфер памяти в качестве постоянного тока для GDI

StackOverflow https://stackoverflow.com/questions/91511

  •  01-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Итак, как мне сделать (a) или (b)?

Спасибо!

Это было полезно?

Решение

Хорошо, перейдем к рассмотрению двух частей проблемы.

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

Чтобы преобразовать содержимое в 8 бит в секунду, вам потребуется использовать систему.Рисование.Визуализация.Класс 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!

Другие советы

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

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

То есть.нет необходимости копировать память из буфера "простой памяти" в растровое изображение CreateDIBSection перед блиттингом, если вы изначально используете растровое изображение CreateDIBSection вместо буфера "простой памяти".

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top