Намалюйте один піксель у Windows Forms


93

Я застряг, намагаючись увімкнути один піксель у формі Windows.

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, або +, або невелике поле. Але для неважливих даних я просто хочу один піксель.
Марк Т

Всі відповіді, які я бачу, здаються такими, ніби вони справді надмірні для одного пікселя? Чому здається простішим просто зробити буфер [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
@R. Бемроуз: Насправді, це залежить від об’єкта «Перо», якому ви передаєте DrawRectangle(). Якщо він є, Pen.Alignment = PenAlignment.Outsetвін намалює Pen.Widthпікселі товстим контуром навколо заданого прямокутника. Але якщо ви вкажете, Pen.Alignment = PenAlignment.Insetвін намалює Pen.Width пікселів товщиною "контуру" всередині заданого прямокутника.
Cipi

2
Це дуже незадовільна відповідь. Хоча це встановлює один піксель, не завжди потрібно встановлювати буквальний піксель. Увесь час, коли мені це було потрібно, що часто буває, мені потрібно було, щоб крапку намалювала та сама ручка, якою малюються лінії, а не пензлик. Іншими словами, нам потрібен рядок, який коротший за 2 пікселі. Я підозрюю, що саме це було потрібно ОП.
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 / whatnot, але замість цього ви можете використовувати DrawRectangle з шириною та висотою, встановленими на 1, щоб намалювати один піксель.


2
Приємна спроба, але при розмірі 1 він малює 2х2 пікселі. (Намалюйте його поруч із лінією, і ви бачите, що вона вдвічі ширша.) І з нульовим розміром, звісно, ​​вона нічого не малює.
Mark T

Правильно, моя помилка. :) Добре, що FillRectangle робить трюк.
Ритміс,

0

Малювання лінії 2px за допомогою пера за допомогою 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.