在Windows窗体上绘制一个像素

时间:2009-04-17 15:57:08

标签: c# .net winforms gdi+ pixel

我试图打开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#。

9 个答案:

答案 0 :(得分:108)

这将设置一个像素:

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

答案 1 :(得分:17)

Graphics对象没有这个,因为它是一个抽象,可以用来覆盖矢量图形格式。在这种情况下,设置单个像素是没有意义的。 Bitmap图片格式包含GetPixel()SetPixel(),但不包含基于其中的图形对象。对于您的场景,您的选项看起来确实是唯一的选项,因为没有一种通用的方式为一般图形对象设置单个像素(并且您不完全知道它是什么,作为您的控件/表单可以是双缓冲等等。)

为什么需要设置一个像素?

答案 2 :(得分:17)

只是为了显示Henk Holterman答案的完整代码:

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

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

答案 3 :(得分:9)

在我绘制大量单个像素(用于各种自定义数据显示)的情况下,我倾向于将它们绘制到位图,然后将其显示在屏幕上。

Bitmap GetPixel和SetPixel操作并不是特别快,因为它们进行了大量的边界检查,但是很容易制作一个可以快速访问位图的“快速位图”类。

答案 4 :(得分:1)

显然,DrawLine绘制的线条与实际指定长度相差一个像素。似乎没有DrawPoint / DrawPixel / whatnot,但您可以使用宽度和高度设置为1的DrawRectangle来绘制单个像素。

答案 5 :(得分:1)

MSDN Page on GetHdc

我认为这就是你要找的东西。您将需要获取HDC,然后使用GDI调用来使用SetPixel。注意,GDI中的COLORREF是存储BGR颜色的DWORD。没有alpha通道,它不是像GDI +的Color结构那样的RGB。

这是我为完成相同任务而编写的一小段代码:

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

答案 6 :(得分:1)

如果您在使用 SmoothingMode = AntiAlias 的图形上绘图,大多数绘图方法将绘制多个像素。如果您只想绘制一个像素,请创建一个 1x1 位图,将位图的像素设置为所需的颜色,然后在图形上绘制位图。

using (var pixel = new Bitmap(1, 1, e.Graphics))
{
    pixel.SetPixel(0, 0, color);
    e.Graphics.DrawImage(pixel, x, y);
}

答案 7 :(得分:0)

这里有很多答案,但它们都非常糟糕:(

绝对最好的方法是创建一个位图并将其传递给现有数组的 intptr(指针)。这允许数组和位图数据共享相同的内存......无需'Bitmap.lockbits'/'Bitmap.unlockbits'(慢)。

这是大纲...

  1. 将您的函数标记为“不安全”并将您的项目构建设置设置为允许“不安全”代码! (c# 指针)
  2. 创建你的数组[,]。使用 Uint32、字节或允许 Uint32 或单个 Uint8 访问的结构 (通过使用显式字段偏移量)
  3. 使用“System.Runtime.InteropServices.Marshal.UnsaveAddrOfPinnedArrayElement”获取数组开头的 Intptr。
  4. 使用采用 Intptr 和 Stride 的构造函数创建位图。这将使新位图与现有数组数据重叠。

您现在可以永久直接访问像素数据!

底层数组

底层数组可能是用户结构“像素”的二维数组。为什么?嗯...结构体可以通过使用显式的固定偏移量来允许多个成员变量共享相同的空间!这意味着该结构可以有 4 个单字节成员(.R、.G、.B 和 .A),以及 3 个重叠的 Uint16(.AR、.RG 和 ,GB)……和一个 Uint32(.ARGB ) ...这可以使色彩平面操作更快。

由于 R、G、B、AR、RG、GB 和 ARGB 都访问同一 32 位像素的不同部分,因此您可以以高度灵活的方式操作像素!

因为 Pixel[,] 数组与 Bitmap 本身共享相同的内存,Graphics 操作会立即更新 Pixel 数组 - 对数组的 Pixel[,] 操作会立即更新位图!您现在有多种操作位图的方法;)

灵活快速;)

请记住,通过使用这种技术,您不需要使用“lockbits”来将位图数据编组进出缓冲区……这很好,因为 lockbits 非常慢。

您也不需要使用画笔并调用能够绘制图案化、可缩放、可旋转、可平移、可别名、矩形……的复杂框架代码,只需编写单个像素相信我 - 图形中的所有灵活性类使使用“Graphics.FillRect”绘制单个像素成为一个非常缓慢的过程。

其他好处...

超级流畅的滚动!您的像素缓冲区在高度和宽度上都可以大于画布/位图!这可以实现高效滚动!!!!

怎么样?

好吧,当您从数组创建位图时,您可以通过获取该 Pixel[,] 的 IntPtr 将位图左上角坐标指向任意 [y,x] 坐标。

然后,通过故意设置位图的“步幅”以匹配数组的宽度(不是位图的宽度),您可以渲染较大数组的预定义子集矩形......同时绘制(提前)进入看不见的边缘!这就是平滑滚动中“离屏绘制”的原理。

终于

您真的应该将 Bitmap 和 Array 包装到 FastBitmap 类中。这将帮助您控制数组/位图对的生命周期。显然,如果数组超出范围或被破坏 - 位图将指向非法内存地址。通过将它们包装在 FastBitmap 类中,您可以确保不会发生这种情况...

...它也是一个非常方便的地方,可以放置您不可避免地想要添加的各种实用程序...例如滚动、淡入淡出、使用色彩平面等。

记住:

  1. 从 MemoryStream 创建位图非常慢
  2. 使用 Graphics.FillRect 绘制像素非常低效
  3. 使用锁定像素/解锁像素访问底层位图数据非常慢
  4. 而且,如果您正在使用“System.Runtime.InteropServices.Marshal.Copy”,请停止!

将位图映射到一些现有的阵列内存是要走的路。做对了,您将永远不需要/不想再次使用框架位图:)

不客气;)

答案 8 :(得分:-1)

使用带有DashStyle.DashStyle.Dot的笔绘制线条2px绘制单个像素。

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