Нарисуйте один пиксель в Windows Forms


93

Я застрял, пытаясь включить один пиксель в Windows Form.

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

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

У API действительно должен быть метод для установки цвета одного пикселя, но я его не вижу.

Я использую C #.


Я сделал элемент управления, который строит очень хорошие научные графики (с дополнительными возможностями, которые мне не нужны ни в каких коммерческих элементах управления). Я могу построить точку данных с помощью и X, или +, или маленького прямоугольника. Но для неважных данных мне нужен только один пиксель.
Mark T

Все ответы, которые я вижу, кажутся излишними для одного пикселя? Почему кажется проще сделать буфер [y * width + x] = color;
NoMoreZealots

Ответы:


114

Это установит один пиксель:

e.Graphics.FillRectangle(aBrush, x, y, 1, 1);

18
Кто бы мог подумать, что DrawRectangle рисует 2x2, а FillRectange рисует 1x1 с теми же аргументами? Спасибо.
Mark T

9
@Mark: В этом есть своего рода смысл ... DrawRectangle говорит: «Нарисуйте контур, начинающийся с x и y с этой шириной и высотой», поэтому он рисует как начальную, так и конечную точки (x, y; x + 1, y ; x, y + 1; x + 1, y + 1) и линии между ними. FillRectangle говорит: «Нарисуйте твердую поверхность, начиная с x, y этой ширины и высоты». Между прочим, FillRectangle использует кисть, а не перо.
Powerlord

5
@Р. Бемроуз: На самом деле, это зависит от объекта Pen, к которому вы переходите DrawRectangle(). Если он есть, Pen.Alignment = PenAlignment.Outsetон будет рисовать Pen.Widthтолстый контур пикселей вокруг данного прямоугольника. Но если вы укажете, Pen.Alignment = PenAlignment.Insetон будет рисовать «контур» Pen.Width пикселей толщиной внутри данного прямоугольника.
Cipi

2
Это очень неудовлетворительный ответ. Хотя это устанавливает один пиксель, не всегда требуется устанавливать буквальный пиксель. Все время, когда мне это было нужно, а часто мне нужно, чтобы точка рисовалась тем же пером, которым рисуются линии, а не кистью. Другими словами, нам нужна линия короче 2 пикселей. Я подозреваю, что это то, что нужно OP.
RobertT

6
Мне не нравится этот ответ. Этот способ крайне неэффективен.
user626528

19

Просто чтобы показать полный код ответа Хенка Холтермана:

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

g.FillRectangle(aBrush, x, y, 1, 1);

18

У Graphicsобъекта этого нет, поскольку он является абстракцией и может использоваться для покрытия формата векторной графики. В этом контексте установка одного пикселя не имеет смысла. Формат Bitmapизображения имеет GetPixel()иSetPixel() , но не графический объект, построенный на нем. Для вашего сценария ваш вариант действительно кажется единственным, потому что нет универсального способа установить один пиксель для общего графического объекта (и вы не знаете ТОЧНО, что это такое, поскольку ваш элемент управления / форма может быть с двойной буферизацией и т. д.)

Зачем нужно ставить один пиксель?


9

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

Операции Bitmap GetPixel и SetPixel не особенно быстрые, потому что они выполняют очень много проверок границ, но довольно легко создать класс «быстрого растрового изображения», который имеет быстрый доступ к растровому изображению.


2

Страница MSDN на GetHdc

Я думаю, это то, что вы ищете. Вам нужно будет получить HDC, а затем использовать вызовы GDI для использования SetPixel. Обратите внимание, что COLORREF в GDI - это DWORD, в котором хранится цвет BGR. Нет альфа-канала, и это не RGB, как цветовая структура GDI +.

Это небольшой фрагмент кода, который я написал для выполнения той же задачи:

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

1

Очевидно, DrawLine рисует линию, длина которой на один пиксель меньше фактической указанной длины. Кажется, нет DrawPoint / DrawPixel / еще чего-то, но вместо этого вы можете использовать DrawRectangle с шириной и высотой, установленными на 1, для рисования одного пикселя.


2
Хорошая попытка, но с размером 1 он рисует 2x2 пикселя. (Нарисуйте его рядом с линией, и вы увидите, что он вдвое шире.) А с нулевым размером, конечно, ничего не рисует.
Mark T

Верно, моя ошибка. :) Хорошо, что FillRectangle делает свое дело.
Rytmis

0

Рисование линии 2 пикселя с помощью пера с DashStyle.DashStyle.Dot рисует один пиксель.

    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);
        }
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.