Pergunta

Eu tenho uma memória tampão correspondente à minha resolução de tela (1280x800 com 24-bits por pixel) que contém meus conteúdo da tela em 24bpp. Eu quero converter isso em 8-bpp (paleta de cores ie. Halftone no Windows). Actualmente, eu fazer isso: 1. Use CreateDIBSection para alocar um novo 1280x800 tampão 24 bpp e acessá-lo como um DC, bem como um buffer de memória simples 2. Use memcpy para copiar do meu tampão original para este novo tampão do passo 1 3. Use BitBlt para deixar GDI realizar a conversão de cores

Eu quero evitar a memcpy extra de etapa 2. Para fazer isso, eu posso pensar de duas abordagens:

a. Envolver minha buf mem original em um DC para executar BitBlt diretamente dele

b. Escrever meu próprio 24 bpp a conversão de cores 8-bpp. Não consigo encontrar qualquer informação sobre como o Windows executa esta conversão de cores de meio-tom. Além mesmo se eu descobrir, eu não vou estar usando as características aceleradas de GDI que BitBlt tem acesso.

Então, como eu faço qualquer um (a) ou (b)?

Obrigado!

Foi útil?

Solução

OK, para abordar as duas partes do problema.

  1. os seguintes mostra o código como chegar aos pixels dentro de um bitmap, alterá-los e colocá-los de volta para o bitmap. Você pode sempre gerar um bitmap manequim do tamanho e formato correto, abri-lo, copie os seus dados e então você tem um objeto de bitmap com seus dados:

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

Para converter o conteúdo para 8bpp você vai querer usar a classe System.Drawing.Imaging.ColorMatrix. Eu não tenho em mãos os valores da matriz corretos para meio-tom, mas este exemplo grayscales e ajuste dos valores deve lhe dar uma idéia do efeito:

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

Vou tentar e atualizar mais tarde com os valores da matriz de meios-tons, é provável que seja lotes 0,5 ou 0,333 valores em lá!

Outras dicas

Use CreateDIBitmap em vez de CreateDIBSection.

Se você quiser eliminar a cópia (passo 2), basta usar CreateDIBSection para criar seu buffer de memória original em primeiro lugar. Então você pode apenas criar um DC compatível para esse bitmap e usá-lo como fonte para a operação BitBlt.

i. não há necessidade de copiar a memória de uma "memória plain" tampão a um bitmap CreateDIBSection antes blitting se você usar um bitmap CreateDIBSection em vez de uma "memória plain" tampão em primeiro lugar.

Afinal, um buffer alocado usando CreateDIBSection é essencialmente apenas uma "memória plain" tampão que seja compatível com CreateCompatibleDC, que é o que você está procurando.

Como você conseguiu o conteúdo da tela em este buffer de memória 24bpp em primeiro lugar?

A via óbvia de evitar uma desnecessária memcpy é subverter o screengrab original por criar o DIBSection 24bpp primeiro, e passá-la para a função screengrab como o tampão de destino.

Se isso não for possível, você ainda pode tentar e coagir GDI em fazer o levantamento duro, criando um BITMAPINFOHEADER que descreve o formato do buffer de memória, e apenas chamar StretchDIBits para blit-lo em seu DIBSection 8bpp.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top