Desenhe um único pixel no Windows Forms

92

Estou tentando ligar um único pixel em um Windows Form.

graphics.DrawLine(Pens.Black, 50, 50, 51, 50); // draws two pixels

graphics.DrawLine(Pens.Black, 50, 50, 50, 50); // draws no pixels

A API realmente deveria ter um método para definir a cor de um pixel, mas não vejo nenhum.

Estou usando C #.

Mark T
fonte
Fiz um controle que traça alguns gráficos científicos muito bons (com recursos adicionais, não preciso estar disponível em nenhum controle comercial). Posso plotar um ponto de dados com e X ou a + ou uma pequena caixa. Mas, para dados sem importância, quero apenas um único pixel.
Mark T
Todas as respostas que vejo parecem realmente exageradas para um único pixel? Por que parece mais fácil simplesmente fazer um buffer [y * largura + x] = cor;
NoMoreZealots de

Respostas:

113

Isso definirá um único pixel:

e.Graphics.FillRectangle(aBrush, x, y, 1, 1);
Henk Holterman
fonte
18
Quem poderia imaginar que DrawRectangle desenha 2x2, mas FillRectange desenha 1x1 com os mesmos argumentos? Obrigado.
Mark T
9
@Mark: Faz sentido ... DrawRectangle diz "desenhe um contorno começando em xey desta largura e altura", então desenha os pontos inicial e final (x, y; x + 1, y ; x, y + 1; x + 1, y + 1) e linhas entre eles. FillRectangle diz "desenhe uma superfície sólida começando em x, y desta largura e altura." A propósito, FillRectangle leva um pincel, não uma caneta.
Powerlord
5
@R. Bemrose: Na verdade, depende do objeto Pen que você passa DrawRectangle(). Se tiver, Pen.Alignment = PenAlignment.Outsetele desenhará Pen.Widthum contorno grosso em pixels ao redor do retângulo fornecido. Mas se você especificar, Pen.Alignment = PenAlignment.Insetele desenhará Pen.Width pixels de espessura "contorno" dentro do retângulo dado.
Cipi
2
Esta é uma resposta muito insatisfatória. Embora isso defina um único pixel, o requisito nem sempre é definir um pixel literal. Todas as vezes que precisei disso, o que é frequente, precisei que o ponto fosse desenhado com a mesma caneta com que as linhas são desenhadas, não um pincel. Em outras palavras, precisamos de uma linha menor que 2 pixels. Suspeito que era disso que o OP precisava.
RobertT
6
Eu não gosto dessa resposta. Dessa forma, é altamente ineficiente.
user626528
18

O Graphicsobjeto não tem isso, pois é uma abstração e poderia ser usado para cobrir um formato de gráfico vetorial. Nesse contexto, definir um único pixel não faria sentido. O Bitmapformato da imagem tem GetPixel()e SetPixel(), mas não um objeto gráfico construído em um. Para o seu cenário, sua opção realmente parece ser a única porque não existe uma maneira única de definir um único pixel para um objeto gráfico geral (e você não sabe EXATAMENTE o que é, como seu controle / forma pode ter buffer duplo, etc.)

Por que você precisa definir um único pixel?

Adam Robinson
fonte
18

Apenas para mostrar o código completo para a resposta de Henk Holterman:

Brush aBrush = (Brush)Brushes.Black;
Graphics g = this.CreateGraphics();

g.FillRectangle(aBrush, x, y, 1, 1);
WoodyDRN
fonte
9

Onde estou desenhando muitos pixels únicos (para várias exibições de dados personalizadas), tendo a desenhá-los em um bitmap e, em seguida, colocá-los na tela.

As operações Bitmap GetPixel e SetPixel não são particularmente rápidas porque fazem muitas verificações de limites, mas é muito fácil criar uma classe de 'bitmap rápido' que tenha acesso rápido a um bitmap.

Will Dean
fonte
2

Página MSDN em GetHdc

Eu acho que isto é o que você está procurando. Você precisará obter o HDC e então usar as chamadas GDI para usar SetPixel. Observe que um COLORREF em GDI é um DWORD que armazena uma cor BGR. Não há canal alfa e não é RGB como a estrutura de cores do GDI +.

Esta é uma pequena seção de código que escrevi para realizar a mesma tarefa:

public class GDI
{
    [System.Runtime.InteropServices.DllImport("gdi32.dll")]
    internal static extern bool SetPixel(IntPtr hdc, int X, int Y, uint crColor);
}

{
    ...
    private void OnPanel_Paint(object sender, PaintEventArgs e)
    {
        int renderWidth = GetRenderWidth();
        int renderHeight = GetRenderHeight();
        IntPtr hdc = e.Graphics.GetHdc();

        for (int y = 0; y < renderHeight; y++)
        {
            for (int x = 0; x < renderWidth; x++)
            {
                Color pixelColor = GetPixelColor(x, y);

                // NOTE: GDI colors are BGR, not ARGB.
                uint colorRef = (uint)((pixelColor.B << 16) | (pixelColor.G << 8) | (pixelColor.R));
                GDI.SetPixel(hdc, x, y, colorRef);
            }
        }

        e.Graphics.ReleaseHdc(hdc);
    }
    ...
}
nem
fonte
1

Aparentemente, DrawLine desenha uma linha que é um pixel menor do comprimento real especificado. Não parece haver DrawPoint / DrawPixel / whatnot, mas em vez disso, você pode usar DrawRectangle com largura e altura definidas como 1 para desenhar um único pixel.

Rytmis
fonte
2
Boa tentativa, mas com o tamanho 1 desenha 2x2 pixels. (Desenhe ao lado de uma linha e você verá que tem o dobro da largura.) E com tamanho zero, é claro, ele não desenha nada.
Mark T
Certo, erro meu. :) Ainda bem que FillRectangle resolve o problema.
Rytmis,
0

Desenhar uma linha 2px usando uma caneta com DashStyle.DashStyle.Dot desenha um único Pixel.

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        using (Pen p = new Pen(Brushes.Black))
        {
            p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            e.Graphics.DrawLine(p, 10, 10, 11, 10);
        }
    }
Eddy
fonte