如何使用Bresenham创建任意厚度的线?

时间:2009-08-03 14:37:03

标签: algorithm graphics

我目前正在使用Bresenham的算法绘制线条,但它们(当然)厚度为一个像素。我的问题是绘制任意厚度线条的最有效方法是什么?

我使用的语言是C.

11 个答案:

答案 0 :(得分:19)

采用另一个Bresenham循环并使用它来修改矩形方向上原始线的起点和终点位置。 问题是有效地找到正确的起点,而不是在绘制下一行时绘制任何像素两次(或跳过一个像素)。

可以从Github C code获得工作和测试的C代码。

这是一个测试页面,包含由此代码创建的一些示例行。 黑色像素是算法的起点。

Test page with bresenham lines with different thickness

答案 1 :(得分:10)

我认为最好的方法是绘制一个矩形而不是一条线,因为一条宽度为二维的对象。要绘制一组平行线以避免过度绘制(以减少写入带宽)和欠拉(缺少像素)将非常复杂。从起点和终点以及宽度计算矩形的角点并不太难。

因此,在下面的评论之后,执行此操作的过程将是: -

  1. 创建一个长度与所需的线宽相同的矩形,宽度等于所需的宽度,所以(0,0)到(宽度,长度)
  2. 使用2D变换
  3. 旋转并将矩形角坐标转换为所需位置
  4. 使用硬件加速渲染器(例如OpenGL quad)或使用软件栅格化器对旋转的矩形进行栅格化。它可以使用四边形栅格化器或一对三角形(例如,左上角和右下角)进行渲染。
  5. 注意*:如果您使用的是OpenGL,您也可以同时执行第2步。当然,使用OpenGL确实意味着理解OpenGL(大而复杂),而这个应用程序可能会在开发的后期阶段实现这个棘手的事情。

答案 2 :(得分:9)

这是Bresenham用于绘制加粗线条的算法的修改版本的paper and Delphi implementation

您可能还想看看Anti-Grain Geometry,这是一个用于2D图形的高质量和高性能软件渲染的库。看看demo page,了解它可以做些什么。

答案 3 :(得分:7)

为获得最佳精度,尤其是较粗线条的良好性能,您可以将线条绘制为多边形。一些伪代码:

draw_line(x1,y1,x2,y2,thickness)
  Point p[4];
  angle = atan2(y2-y1,x2-x1);
  p[0].x = x1 + thickness*cos(angle+PI/2);
  p[0].y = y1 + thickness*sin(angle+PI/2);
  p[1].x = x1 + thickness*cos(angle-PI/2);
  p[1].y = y1 + thickness*sin(angle-PI/2);
  p[2].x = x2 + thickness*cos(angle-PI/2);
  p[2].y = y2 + thickness*sin(angle-PI/2);
  p[3].x = x2 + thickness*cos(angle+PI/2);
  p[3].y = y2 + thickness*sin(angle+PI/2);
  draw_polygon(p,4)

可选择在每个终点绘制一个圆圈。

答案 4 :(得分:4)

一些简单的使用路线:

  1. 表示n为奇数的任何宽度n。对于任何点p,也绘制了n / 2上方/下方的点(如果该线是> 45度角,则相反地绘制)。
    • 不是正确厚度的正确线条,更像斜体笔,但非常快。
  2. 对于起点p(x,y)选取点t0和b使得它们以p为中心但是n个像素分开。对于终点做同样的结果导致t1 b1。从t0 - >绘制线条。 t1,t1-> b1,b1 - > t0,b0 - > T1。填写生成的矩形。
    • 这里的技巧是选择点,使它们看起来与路径方向正交。
  3. 对于线上的每个点p而不是绘制一个点绘制一个圆。
    • 无论方向如何,这都有使端点“干净”的优势。
    • 除了第一个之外,不需要渲染任何实心圆圈。
    • 有点慢

答案 5 :(得分:2)

我假设你会从一条边界线到另一条边界线绘制水平跨度,并按照Bresenham的方法计算每条线的x值(在一个循环中)。

没试过。

终点可能需要引起注意,以免它们看起来奇怪地被切断。

答案 6 :(得分:1)

http://members.chello.at/~easyfilter/bresenham.html

此链接底部的示例是javascript,但应该很容易适应C.这是一种相当简单的抗锯齿算法来绘制可变厚度的线。

答案 7 :(得分:1)

我经常这样做以生成用于多孔介质模拟的纤维和球体的图像。我有一个很简单的方法,使用非常标准的图像分析技术,称为“距离变换”。这需要访问某些图像分析包。我使用Python和Scipy,所以这没问题。这是一个将随机分布的点转换为球体的演示:

import scipy as sp
import scipy.ndimage as spim

im1 = sp.rand(100, 100) < 0.995  # Create random points in space
dt = spim.distance_transform_edt(im1)
im2 = dt < 5  # To create sphere with a radius of 5

random seeds, distance map, final spheres

就是这样!对于非常大的图像,距离变换可能很慢,但是那里有高效的版本。例如,ImageJ有一个并行的。显然,要创建粗纤维,只需创建薄图像,然后应用上面的步骤2和步骤。

答案 8 :(得分:0)

对于我的嵌入式热敏打印机应用程序,使用Bresenham的算法,线条太薄了。我没有GL或任何花哨的东西。我最终简单地减少了Y值并在第一个下面画了更多的线。每个厚度的数量增加了另一条线。非常快速地实现并使所需的结果从单色位图打印到热量。

答案 9 :(得分:0)

前段时间我遇到了同样的问题。 基于此paper,我创建了一个Matlab参考实现,我想在GitHub上分享。

答案 10 :(得分:0)

对于那些想要Python版本的人,这里有代码(基于@Fabel的答案):

def drawline(x1,y1,x2,y2,**kwargs):  
    if kwargs.get('thickness')==None:
        thickness=1
    else:
        thickness=kwargs['thickness']
    if kwargs.get('roundcap')==None:
        roundcap=False
    else:
        roundcap=True
    angle = np.arctan2(y2-y1,x2-x1)
    xx = np.zeros(4)
    yy = np.zeros(4)
    xx[0] = np.round(x1 + thickness*np.cos(angle+np.pi/2))
    yy[0] = np.round(y1 + thickness*np.sin(angle+np.pi/2))
    xx[1] = np.round(x1 + thickness*np.cos(angle-np.pi/2))
    yy[1] = np.round(y1 + thickness*np.sin(angle-np.pi/2))
    xx[2] = np.round(x2 + thickness*np.cos(angle-np.pi/2))
    yy[2] = np.round(y2 + thickness*np.sin(angle-np.pi/2))
    xx[3] = np.round(x2 + thickness*np.cos(angle+np.pi/2))
    yy[3] = np.round(y2 + thickness*np.sin(angle+np.pi/2))
    u,v=polygon(xx,yy)    
    if roundcap:
        temp1x, temp1y = circle(x1,y1,thickness)
        temp2x, temp2y = circle(x1,y1,thickness)
        u = np.append(u,temp1x,temp2x)
        v = np.append(v,temp1y,temp2y)
    return u,v

调用该函数时,可以选择指定厚度和圆角。例如:

drawline(10,10,50,50,thickness=3,roundcap=False)