不能使用GDI +。可以使用Windows API调用在位图上绘制线条吗?

时间:2011-01-01 08:13:56

标签: c# bitmap bitmapdata

我们有一个用C ++ 6编写的应用程序,它在流量图上填充空白多边形以显示当前的交通状况。绿色为好,黄色更拥挤,等等。

“shell地图”是使用矩形和多边形的道路的位图,这些矩形和多边形中没有任何东西填充。从道路传感器(线路电感器回路)收集数据,并根据回路检测器数据填充多边形。

当地图发生变化时,有人必须手动放大绘画中的位图,并获取每个新形状内部的坐标,其中将填充拥挤颜色。制作地图骨架的多边形都被绘制在海军蓝色,在白色背景上。

我做了一个应用程序。当用户点击多边形的白色部分内的任何位置时,内部周边的点将显示给用户,其中放大的缩略图显示内部许可者被绘制。

在.Net中,周边画得很完美。

在C ++ 6应用程序中,一些polyon指向我的应用程序。收集不能正确显示。

我查看了msPaint,而.Net并没有像MS Paint一样绘制点。

这是一个简单的例子。代码来自一个带有一个按钮和一个标签的表单。在位图上绘制一条线,位图“放大”,以便您可以看到它,并显示在标签上。

如果使用相同的两个点在Paint中绘制一条线,则绘制的线段与在MS Paint中绘制的线段不同。

  private void button1_Click(object sender, EventArgs e)
    {
        Bitmap bm = new Bitmap(16, 16);
        Graphics g = Graphics.FromImage(bm);
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Default;
        Point pt = new Point(1, 3);
        Point pt2 = new Point(5, 1);
        // If you reverse the points, the same line is drawn.
        g.DrawLine(Pens.Orange, pt, pt2);
        // label1.Image = bm;
        Bitmap bm2 = ZoomMap(8, bm);
        g.Dispose();
        bm.Dispose();
        label1.Image = bm2;
    }

    private Bitmap ZoomMap(int zoomValue, Bitmap bm)
    {
        Bitmap zoomedBitMap;
        Size sz = bm.Size;// new Size(75, 75);
        Size newSize = new Size(sz.Width * zoomValue, sz.Height * zoomValue);
        zoomedBitMap = new Bitmap(newSize.Width, newSize.Height);
        Graphics gr = Graphics.FromImage(zoomedBitMap);
        gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
        gr.PageUnit = GraphicsUnit.Pixel;
        gr.DrawImage(bm, 1, 1, newSize.Width, newSize.Height);
        gr.Dispose();
        return zoomedBitMap;
    }

alt text

无论我应用什么设置,都无法在C#中模仿MS Paint,但在C ++ 6中完美模仿MS Paint。

我可以调用任何类型的Windows API来绘制现有的位图吗?

提前感谢您的回复。

1 个答案:

答案 0 :(得分:4)

使用数字差分分析器(DDA)或Bresenham算法将得到与GDI +相同的结果,但是如果你看一下标准的GDI线条图实现,你会注意到实际的标准GDI LineTo实现绘制比您指定的短一个像素的行,引用MSDN

' LineTo函数从当前位置绘制一条直线,但不包括指定的点。'

因此,使用GDI你的线是从(1,3) - (4,1)绘制的,如果你在GDI +中使用这些相同的坐标,你会看到线的像素结构与你在旧版中看到的相匹配Paint的版本,当然除了(5,1)的最后一个像素没有绘制。像Paint这样的程序只需添加额外的像素即可完成该行。

使用直接GDI会要求您处理互操作以创建笔,选择对象进入DC删除对象等所有这些都很可能但最终不方便。您可以在GDI +中通过绘制一个像素短的线然后从该端点绘制一条线到最终端点来模拟这个,这将填充最后一个像素。

注意:我说的是旧版本的Paint,因为如果你在Windows 7中使用Paint,你会看到正确绘制了这条线。

为了好玩,我想我会很快展示你如何使用.NET中的经典GDI,这里有一些互操作代码。

[DllImport("gdi32")]
static extern bool MoveToEx(IntPtr hdc, int x, int y, out Point point);

[DllImport("gdi32")]
static extern bool LineTo(IntPtr hdc, int x, int y);

[DllImport("gdi32")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

[DllImport("gdi32")]
static extern IntPtr CreatePen(int penStyle, int width, int color);

[DllImport("gdi32")]
static extern bool DeleteObject(IntPtr hgiobj);

private void Form1_Paint(object sender, PaintEventArgs e)
{
  Point ignore;
  IntPtr hdc = e.Graphics.GetHdc();
  IntPtr pen = CreatePen(1, 1, ColorTranslator.ToWin32(Color.Red));
  IntPtr oldPen = SelectObject(hdc, pen);
  MoveToEx(hdc, 1, 3, out ignore);
  LineTo(hdc, 5, 1);                
  SelectObject(hdc, oldPen);
  DeleteObject(pen);      
}

注意该线实际上是一个像素短。就个人而言,我会使用图形设备使用原生.NET解决方案,并使用正确的线坐标来复制clasic GDI功能。

我认为你可能有兴趣知道为什么GDI会离开最后一个像素,通常使用XOR在屏幕上绘制一条线,这样可以非常简单地删除同一条线,只需用XOR重绘线条就可以了删除时原始背景完好无损。当然,这需要注意不要两次画一个像素,否则你会在屏幕上留下文物。

由于通常使用多个LineTo调用来绘制形状,现在可以调用LineTo 3时间来绘制三角形,而不用担心每行的第一个点将与最后一个点重叠。例如,当使用XOR时,前一行会导致所述伪像,随后对LineTo的调用可以安全地从前一行结束的位置开始,因为未绘制前一行的最后一个像素。