【问题标题】:Can't use GDI+. Can a line be drawn on a bitmap using Windows API call?无法使用 GDI+。可以使用 Windows API 调用在位图上绘制一条线吗?
【发布时间】:2011-01-01 08:13:56
【问题描述】:

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

“壳图”是道路的位图,使用矩形和多边形,其中没有填充任何内容。数据从道路传感器(线感应器环路)收集,并根据环路检测器数据填充多边形。

当地图发生变化时,必须有人在paint中手动放大位图,并获取每个新形状内部周围的坐标,其中将填充拥塞颜色。构成地图骨架的多边形都被绘制出来深蓝色,白色背景。

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

在 .Net 中,边界被完美地绘制。

在 C++ 6 应用程序中,一些多边形指向我的应用程序。收藏无法正确显示。

我查看了 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;
    }

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

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

提前感谢您的任何回复。

【问题讨论】:

  • 如果您将 .Net 绘制的图像旋转 180 度以上,您会得到相同的结果。因此,如果您在画布上绘画,然后旋转它并覆盖它,它看起来会产生您想要的效果。

标签: c# bitmap bitmapdata


【解决方案1】:

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

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

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

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

注意:我说的是旧版本的画图,因为如果你在 Windows 7 中使用画图,你会看到画线是正确的。

为了好玩,我想我会快速展示如何使用 .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 解决方案,只需确定正确的线坐标即可复制经典 GDI 功能。

我想你可能想知道为什么 GDI 会留下最后一个像素,通常使用 XOR 在屏幕上画一条线,这会使删除同一条线变得非常简单,只需使用 XOR 重画线即可在原始背景完好无损的情况下被移除。当然,您需要注意不要在一个像素上绘制两次,否则您会在屏幕上留下伪影。

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

【讨论】:

  • 对不起,它在谷歌浏览器中输入,它回复了。我能够使用您发布的功能,并且 GDI 的工作方式与您所说的完全一样。我制作了一个快速示例表单,它在位图上绘制多边形并使用 GDI 为该位图设置标签。感谢您发布的重要信息和示例代码。
  • 看来我将无法发布我的代码 - 此页面不断抛出错误。我稍后再试。
  • @Matt Fomich,我很高兴能帮上忙。
猜你喜欢
  • 2022-08-17
  • 2012-11-09
  • 2021-11-09
  • 1970-01-01
  • 2014-04-11
  • 2014-11-10
  • 2011-11-18
  • 2010-09-16
相关资源
最近更新 更多