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





Хорошо, чтобы решить две части проблемы.
следующий код показывает, как получить пиксели внутри растрового изображения, изменить их и вернуть обратно в растровое изображение. Вы всегда можете сгенерировать фиктивное растровое изображение правильного размера и формата, открыть его, скопировать свои данные, и тогда у вас будет объект растрового изображения с вашими данными:
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!
Я так считаю, пока вы не снимаете блокировку, она фиксируется сборщиком мусора.
Используйте CreateDIBitmap вместо CreateDIBSection.
Если вы хотите удалить копию (шаг 2), просто используйте CreateDIBSection, чтобы в первую очередь создать исходный буфер памяти. Затем вы можете просто создать совместимый контроллер домена для этого растрового изображения и использовать его в качестве источника для операции BitBlt.
Т.е. нет необходимости копировать память из буфера «простой памяти» в битовую карту CreateDIBSection перед копированием, если вы в первую очередь используете битовую карту CreateDIBSection вместо буфера «простой памяти».
В конце концов, буфер, выделенный с помощью CreateDIBSection, по сути, является просто буфером «простой памяти», совместимым с CreateCompatibleDC, что вы и ищете.
Описание процедуры CreateDIBSection в томе 3 Справочника программиста Win32 полностью неверно. Правильное описание см. На веб-сайте. docs.microsoft.com/en-us/windows/desktop/api/wingdi/…
Как вы вообще поместили содержимое экрана в этот буфер памяти 24bpp?
Очевидный способ избежать ненужного memcpy - это разрушить исходный снимок экрана, сначала создав 24bpp DIBSection и передав его функции screengrab в качестве целевого буфера.
Если это невозможно, вы все равно можете попытаться заставить GDI выполнить жесткую работу, создав BITMAPINFOHEADER, описывающий формат буфера памяти, и просто вызовите StretchDIBits, чтобы перенести его на свой 8bpp DIBSection.
Можно ли передать
Scan0в неуправляемый код для работы?