GDI+线条绘制算法

3

我无法理解GDI+在表面上绘制线条的方式,可能它有一些算法来完成这个过程。

例如,让我们来看一个10x10像素的表面。

  Bitmap img = new Bitmap(10, 10);

现在让我们在这个表面上画一条线,宽度为5px,顶部偏移量为5px。
  using (var g = Graphics.FromImage(img))
     {
        g.Clear(Color.White);
        var pen = new Pen(Color.Brown);
        pen.Width = 5;
        g.DrawLine(pen, 0F, 5F, 10F,  5F);
     }

我们将得到: enter image description here 这张图并不是从像素#5开始绘制的,它是从像素#4开始绘制的。 很明显,起始点是单独计算的。但是如何计算呢?
我试图找到规律,得到了这个:
  y = offset + width/2 - 1

其中y是真实起点y,offset是选择的起点y。

但有些情况下这种方法行不通。例如,假设宽度为6,选择的顶部偏移量为0,我们将得到y=2,并且会被绘制成这样:

enter image description here

它应该显示6个像素,但却没有。

因此,必须有更一般的算法来选择起始点,但我真的不知道它可以是什么。任何帮助都将不胜感激。

3个回答

3

直线绘制中没有偏移量offset。在DrawLine方法中指定的坐标定义了直线的中心。顶部像素为y - width / 2,底部像素为y - width / 2 + width - 1。第二个公式考虑了width / 2被舍去的事实。此外,顶行是y = 0,底行是y = 9。所以,对于您的第一行:

top = 5 - (5 / 2) = 3
bottom = 5 - (5 / 2) + 5 - 1 = 7

以及第二行:

top = 2 - (6 / 2) = -1
bottom = 2 - (6 / 2) + 6 - 1 = 4

顶部边缘被剪裁到位图的边缘,因此线宽减小。

1
在第一个示例中,看起来像是一条宽度为5个像素的线,在第5行居中(从0开始计数,而不是1)。 这似乎是一个合理的结果。
在第二个示例中,看起来像是一条宽度为6的线,在第1行和第2行之间居中,其中顶部行被裁剪掉,因为它超出了图像的边界。

0

GDI+中的坐标并不指代像素本身,而是它们中心的(无限小的)点。因此,(0f,5f)表示第一列和第六行像素的中心(因为从零开始计数)。因此,我将从现在开始区分坐标像素行

绘制线条时,GDI+概念上沿着由这些坐标定义的线移动指定宽度的笔。请注意,您可以通过指定Pen.StartCapPen.EndCap来定义线的开头和结尾处的笔的确切形状。

既然您指定了宽度 5f 并且正在绘制一条水平线,该线将向上和向下延伸 2.5 像素,从而覆盖从 #3 到(包括)#7 的完整像素行。请注意,像素行 #3 的上边缘具有 y 坐标 2.5,而行 #7 的下边缘根据上述定义为 7.5,分别为 5f-2.5f5f+2.5f

对于您的第二个示例(我在 Try GDI+ application 中尝试),我得到的结果与您不同。相反,我得到了一条覆盖前三个像素行的线。从理论上讲,它甚至应该覆盖前 3.5 行像素(因为坐标表示第一行的中心,上部被简单地裁剪)。但是,如果关闭了抗锯齿功能,则底部的“半行”将被截断。

这可以通过将g.SmoothingMode设置为SmoothingMode.AntiAlias来显示,此时第四行是透明绘制的。当使用抗锯齿时,您还会注意到,由于起始坐标再次位于列的中心,因此第一列和最后一列没有完全绘制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接