无法使用 GDI。可以使用 Windows API 调用在位图上绘制一条线吗?

发布于 2024-10-09 21:49:03 字数 2232 浏览 0 评论 0原文

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

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

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

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

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

alt text

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

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

预先感谢您的回复。

We have an app written in C++ 6 that fills in blank polygons on a traffic map to show current traffic conditions. Green for good, yellow more congested, etc.

The "shell maps" are bitmaps of the roadways using rectangles and polygons that have nothing filled in them. Data is collected from roadway sensors (wire inductor loops) and the polygons are filled based on loop detector data.

When the maps change, someone has to manually zoom in the bitmap in paint, and get the coordinates around the inside of each new shape, where the congestion color will be filled in. The polygons that make the map skeleton are all drawn in navy blue, on a white background.

I made an app. where when the user clicks anywhere inside the white part of the polygon, the points for the inside perimeter are displayed to the user, with a zoomed in thumbnail showing the inside permiter painted.

In .Net, the perimeter is painted perfectly.

In the C++ 6 app, some of the polyon points my app. collects don't display correctly.

I looked at msPaint, and .Net does not draw the points the same way as MS Paint.

Here's a quick example. Code is from one form with one button and one label. A line is drawn on a bitmap, the bitmap is "zoomed in" so you can see it, and displayed on the label.

The line segment drawn is not the same as the line segment that you draw in MS Paint if you draw a line in Paint using the same two points.

  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

No matter what settings I apply, there is no way to mimic MS Paint in C#, but in C++ 6 mimics MS Paint perfectly.

Is there any type of windows API I can call to draw on an existing bitmap?

Thanks in advance for any replies.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

幻想少年梦 2024-10-16 21:49:03

使用数字差分分析器 (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 解决方案,并确定正确的线坐标来复制经典的 GDI 功能。

我想你可能有兴趣知道为什么 GDI 会留下最后一个像素,通常使用 XOR 在屏幕上绘制一条线,这将使删除同一行变得非常简单,只需使用 XOR 重新绘制该线,然后将其删除原来的背景完好无损。当然,这意味着您需要小心,不要在一个像素上绘制两次,否则您会在屏幕上留下伪影。

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

Using either digital differential analizer (DDA) or the Bresenham algorithm will yield the same results you see for GDI+, but if you look at the standard GDI line drawing implementation you will notice that the standard GDI LineTo implementation actually draws the line one pixel shorter than what you specify, to quote the MSDN

'The LineTo function draws a line from the current position up to, but not including, the specified point.'

Because of this, using GDI your line is drawn from (1,3)-(4,1), if you use these same coordinates in GDI+ you will see that the pixel structure of the line matches what you see in older versions of Paint, except of course that the last pixel at (5,1) is not drawn. A program like Paint would then just add the additional pixel to complete the line.

Using straight GDI would require that you handle the interop for creating pens, selecting object into the DC deleteing the objects etc. all very possible but ultimately inconvenient. You could rather simulate this in GDI+ by drawing a line that is one pixel short and then draw a line from that endpoint to the final endpoint, this will fill in the last pixel.

Note: I say older versions of Paint, because if you use Paint in Windows 7 for example you will see that the line is drawn correctly.

For fun I thought I would quickly show how you might use classic GDI from .NET, here is some interop code.

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

Notice how the line is actually one pixel short. Personally I would go with a native .NET solution using the Graphics device and just dermine the correct line coordinate to replicate the clasic GDI functionality.

I thought you might be interested to know why GDI leaves off the last pixel, it was common to use XOR to draw a line on the screen that would make removing the same line very simple, just redraw the line with XOR and it is removed with the original background intact. Of course this ment that you need to be careful not to draw over a pixel twice otherwise you would leave artifacts on the screen.

Since it is common to use draw a shape using multiple LineTo calls you can now call LineTo 3 time to draw a triangle for example and not be concerned that the first point of each line will overlap the last point of the previous line and cause the said artifacts when using XOR for example, susequent calls to LineTo can safely start at where the previous line ended because the last pixel of the previous line was not drawn.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文