Draw a single pixel on Windows Forms
Asked Answered
B

10

100

I'm stuck trying to turn on a single pixel on a Windows Form.

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

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

The API really should have a method to set the color of one pixel, but I don't see one.

I am using C#.

Biquadratic answered 17/4, 2009 at 15:57 Comment(2)
I made a control which plots some very nice scientific graphs (with added capabilities I need not available in any commercial controls). I can plot a data point with and X or a + or a small box. But for unimportant data, I just want a single pixel.Biquadratic
All the answers I see seem like they really are overkill for a single pixel? Why does it seem easier just to do a buffer[y*width+x]=color;Fascicle
S
121

This will set a single pixel:

e.Graphics.FillRectangle(aBrush, x, y, 1, 1);
Suffragette answered 17/4, 2009 at 16:11 Comment(6)
Who would have guessed that DrawRectangle draws a 2x2 but FillRectange draws a 1x1 with the same arguments? Thanks.Biquadratic
@Mark: It does make a sort of sense... DrawRectangle says "draw an outline starting at x and y of this width and height," so it draws both the start and end points (x,y; x+1,y;x,y+1; x+1,y+1) and lines between them. FillRectangle says "draw a solid surface starting at x,y of this width and height." Incidentally, FillRectangle takes a Brush, not a Pen.Iquitos
@R. Bemrose: Actually, it depends on the Pen object you pass to DrawRectangle(). If it has Pen.Alignment = PenAlignment.Outset it will draw Pen.Width pixels thick outline round the given rectangle. But if you specify Pen.Alignment = PenAlignment.Inset it will draw Pen.Width pixels thick "outline" inside the given rectangle.Yama
This is a very unsatisfactory answer. Although this sets a single pixel, the requirement is not always to set a literal pixel. All the times I've needed this, which is often, I've needed the dot to be drawn by the same pen that lines are drawn with, not a brush. In other words, we need a line that is shorter that 2 pixels. I suspect that is what the OP needed.Jalap
I don't like this answer. This way is highly inefficient.Fan
If drawing on graphic with SmoothingMode = AntiAlias, the filled rectangle will start in the middle of the pixel at (x,y) meaning it will draw aliased between 4 pixels. To center the rectangle at (x,y) and thus draw on just the one pixel, subtract 0.5 from x and y. e.Graphics.FillRectangle(aBrush, x - 0.5f, y - 0.5f, 1.0f, 1.0f);Tuna
M
20

The Graphics object doesn't have this, since it's an abstraction and could be used to cover a vector graphics format. In that context, setting a single pixel wouldn't make sense. The Bitmap image format does have GetPixel() and SetPixel(), but not a graphics object built on one. For your scenario, your option really seems like the only one because there's no one-size-fits-all way to set a single pixel for a general graphics object (and you don't know EXACTLY what it is, as your control/form could be double-buffered, etc.)

Why do you need to set a single pixel?

Merv answered 17/4, 2009 at 16:3 Comment(0)
F
19

Just to show complete code for Henk Holterman answer:

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

g.FillRectangle(aBrush, x, y, 1, 1);
Fatigued answered 30/4, 2014 at 21:7 Comment(0)
K
11

Where I'm drawing lots of single pixels (for various customised data displays), I tend to draw them to a bitmap and then blit that onto the screen.

The Bitmap GetPixel and SetPixel operations are not particularly fast because they do an awful lot of boundschecking, but it's quite easy to make a 'fast bitmap' class which has quick access to a bitmap.

Kcal answered 17/4, 2009 at 16:15 Comment(0)
G
4

MSDN Page on GetHdc

I think this is what you are looking for. You will need to get the HDC and then use the GDI calls to use SetPixel. Note, that a COLORREF in GDI is a DWORD storing a BGR color. There is no alpha channel, and it is not RGB like the Color structure of GDI+.

This is a small section of code that I wrote to accomplish the same task:

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);
    }
    ...
}
Gyn answered 13/3, 2015 at 16:23 Comment(0)
G
3

The absolute best method is to create a bitmap and pass it an intptr (pointer) to an existing array. This allows the array and the bitmap data to share the same memory... no need to use Bitmap.lockbits/Bitmap.unlockbits, both of which are slow.

Here's the broad outline:

  1. Mark your function 'unsafe' and set your projects build settings to allow 'unsafe' code! (C# pointers)

  2. Create your array[,]. Either using Uint32's, Bytes, or a Struct that permits access to by both Uint32 OR individual Uint8's (By using explicit field offsets)

  3. Use System.Runtime.InteropServices.Marshal.UnsaveAddrOfPinnedArrayElement to obtain the Intptr to the start of the array.

  4. Create the Bitmap using the constructor that takes an Intptr and Stride. This will overlap the new bitmap with the existing array data.

You now have permanent direct access to the pixel data!

The underlying array

The underlying array would likely be a 2D array of a user-struct Pixel. Why? Well... Structs can allow multiple member variables to share the same space by using explicit fixed offsets! This means that the struct can have 4 single-bytes members (.R, .G, .B, and .A), and 3 overlapping Uint16's (.AR, .RG, and ,GB)... and a single Uint32 (.ARGB)... this can make colour-plane manipulations MUCH faster.

As R, G, B, AR, RG, GB and ARGB all access different parts of the same 32-bit pixel you can manipulate pixels in a highly flexible way!

Because the array of Pixel[,] shares the same memory as the Bitmap itself, Graphics operations immediately update the Pixel array - and Pixel[,] operations on the array immediately update the bitmap! You now have multiple ways of manipulating the bitmap.

Remember, by using this technique you do NOT need to use 'lockbits' to marshal the bitmap data in and out of a buffer... Which is good, because lockbits is very VERY slow.

You also don't need to use a brush and call complex framework code capable of drawing patterned, scalable, rotatable, translatable, aliasable, rectangles... Just to write a single pixel Trust me - all that flexibility in the Graphics class makes drawing a single pixel using Graphics.FillRect a very slow process.

Other benefits

Super-smooth scrolling! Your Pixel buffer can be larger than your canvas/bitmap, in both height, and width! This enables efficient scrolling!

How?

Well, when you create a Bitmap from the array you can point the bitmaps upper-left coordinate at some arbitrary [y,x] coordinate by taking the IntPtr of that Pixel[,].

Then, by deliberately setting the Bitmaps 'stride' to match width of the array (not the width of the bitmap) you can render a predefined subset rectangle of the larger array... Whilst drawing (ahead of time) into the unseen margins! This is the principle of "offscreen drawing" in smooth scrollers.

Finally

You REALLY should wrap the Bitmap and Array into a FastBitmap class. This will help you control the lifetime of the array/bitmap pair. Obviously, if the array goes out of scope or is destroyed - the bitmap will be left pointing at an illegal memory address. By wrapping them up in a FastBitmap class you can ensure this can't happen...

... It's also a really handy place to put the various utilities you'll inevitably want to add... Such as scrolling, fading, working with colour planes, etc.

Remember:

  1. Creating Bitmaps from a MemoryStream is very slow

  2. Using Graphics.FillRect to draw pixels is painfully inefficient

  3. Accessing underlying bitmap data with lockpixels/unlockpixels is very slow

  4. And, if you're using System.Runtime.InteropServices.Marshal.Copy, just stop!

Mapping the Bitmap onto some existing array memory is the way to go. Do it right, and you'll never need/want to use a framework Bitmap again.

Gianna answered 7/6, 2021 at 1:23 Comment(2)
I asked this question 12 years ago, and am still getting answers. This sounds like a very worthwhile method, and I agree it should be made into a class... which I would expect you to have already done. It would really help if you could post the code for such a class and save us from "re-inventing the wheel". (Even if it isn't complete.)Biquadratic
Working code would be really niceSpiritualist
N
2

Drawing a Line 2px using a Pen with DashStyle.DashStyle.Dot draws a single 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);
        }
    }
Nuris answered 22/10, 2016 at 12:9 Comment(0)
T
2

If you are drawing on a graphic with SmoothingMode = AntiAlias, most drawing methods will draw more than one pixel. If you only want one pixel drawn, create a 1x1 bitmap, set the bitmap's pixel to the desired color, then draw the bitmap on the graphic.

using (var pixel = new Bitmap(1, 1, e.Graphics))
{
    pixel.SetPixel(0, 0, color);
    e.Graphics.DrawImage(pixel, x, y);
}
Tuna answered 5/1, 2021 at 15:41 Comment(0)
D
1

Apparently DrawLine draws a line that is one pixel short of the actual specified length. There doesn't seem to be a DrawPoint/DrawPixel/whatnot, but instead you can use DrawRectangle with width and height set to 1 to draw a single pixel.

Deirdredeism answered 17/4, 2009 at 16:7 Comment(2)
Nice try, but with size 1 it draws 2x2 pixels. (Draw it next to a line, and you can see it is twice as wide.) And with size zero, of course, it draws nothing.Biquadratic
Right, my mistake. :) Good thing FillRectangle does the trick.Deirdredeism
L
0

You should put your code inside the Paint event, otherwise, it will not be refreshed and delete itself.

Example:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        Brush aBrush = (Brush)Brushes.Red;
        Graphics g = this.CreateGraphics();

        g.FillRectangle(aBrush, 10, 10, 1, 1);
    }
}
Lamb answered 7/5, 2022 at 14:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.