【问题标题】:How do I draw thick lines with closely spaced points properly with Java2D graphics?如何使用 Java2D 图形正确绘制具有紧密间距点的粗线?
【发布时间】:2012-08-10 17:58:51
【问题描述】:

我正在尝试使用 Java2D 绘制地图。当我的地图被缩小时,我的道路上到处都是绘画文物。这是绘制完整美国州时屏幕的一小部分:

放大后这是一段类似的路段:

所使用的线条样式是一条蓝色实线,其宽度缩放为相当于 2 个像素。 我尝试了各种渲染提示和线连接规则,但似乎没有任何帮助。

我在运行 OS/X 10.8 的 Mac 上使用 Open JDK 1.7,这也可以在装有 Sun JDK 1.6 的 Linux 机器上重现。

Java2D 中的所有形状和变换都尽可能是双精度的。 线条的几何形状有许多紧密间隔的点,我怀疑绘图伪影的原因是渲染器被比单个像素更近的连续点弄糊涂了。

有没有办法在不细化点的情况下改善缩小形状的外观?

编辑 绘图伪影位于单独的线段相交的点,因此丢失的像素与线帽(末端)不相交有关,即使端点相同也是如此。此图像显示了两条线段之间的交汇点。我以 7 像素缩放的线条样式(与白色异或)突出显示了每个线段,但如果您仔细观察,您仍然可以看到原始蓝线的一部分(这是由于圆顶重叠和 XOR 绘制模式.) 在普通比例下,两端似乎重叠,但在普通绘画模式下缩小和返回时,会出现折线效果。

一种解决方法是在绘制之前将所有连续的线段连接在一起,但我仍然想知道绘制伪影的真正原因。

【问题讨论】:

    标签: java graphics java-2d shapes


    【解决方案1】:

    我无法重现您使用 OS X 1.6 JDK 的情况,但我仍有一些建议给您。

    如果您只是使用它来概述状态,请考虑使用GeneralPath class。您可以使用lineTo(x,y) 方法在线上建立每个点。同样,因为我无法使用 Line2D.Double 重新创建您的问题,所以我不知道这是否真的会有所不同。

    其次,可能更重要的是,您是如何放大和缩小的。我在我的Graphics2D 对象上使用了AffineTransform(带有setScaleTo(x,y)),并且一切正常。与通过缩放因子(或您可以做的任何其他事情)缩放数据中的点的替代方法相比,这相当容易。您还必须按因子调整线条的笔划,因为它会缩小所有内容。如果您愿意,我可以发布屏幕截图。

    【讨论】:

    • 感谢您的回复。我正在使用 AffineTransform,它是缩放和平移操作的组合。我也在缩放笔画宽度。我的一些坐标相距 5 - 10m。缩小时画布的全宽约为 1,000,000m。我将看看是否可以回滚到使用 Apple 1.6 JDK 运行的旧版本代码。我主要是在绘制道路时看到问题。县边界看起来不错,但可能没有相同的详细程度。
    • 我也看到了 Apple JDK 1.6 的问题。我开始怀疑这个问题是否与在一条线内绘制人工制品无关,而是与线段的端点有关。仔细观察数据,道路不是具有许多顶点的长连续线,而是许多具有连续端点的较短线段。
    • 接受这个答案,因为它最终让我找到了解决方案。我要么必须关闭我对该图层的一项绘图优化,要么将每条高速公路的路段连接在一起。
    【解决方案2】:

    请检查Xiaolin Wu's line algorithm它应该回答你的问题!

    基本概念

    function plot(x, y, c) is
    plot the pixel at (x, y) with brightness c (where 0 ≤ c ≤ 1)
    
    function ipart(x) is
    return integer part of x
    
    function round(x) is
    return ipart(x + 0.5)
    
    function fpart(x) is
    return fractional part of x
    
    function rfpart(x) is
    return 1 - fpart(x)
    
    function drawLine(x1,y1,x2,y2) is
    dx = x2 - x1
    dy = y2 - y1
    if abs(dx) < abs(dy) then                 
      swap x1, y1
      swap x2, y2
      swap dx, dy
    end if
    if x2 < x1
      swap x1, x2
      swap y1, y2
    end if
    gradient = dy / dx
    
    // handle first endpoint
    xend = round(x1)
    yend = y1 + gradient * (xend - x1)
    xgap = rfpart(x1 + 0.5)
    xpxl1 = xend  // this will be used in the main loop
    ypxl1 = ipart(yend)
    plot(xpxl1, ypxl1, rfpart(yend) * xgap)
    plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap)
    intery = yend + gradient // first y-intersection for the main loop
    
    // handle second endpoint
    xend = round (x2)
    yend = y2 + gradient * (xend - x2)
    xgap = fpart(x2 + 0.5)
    xpxl2 = xend  // this will be used in the main loop
    ypxl2 = ipart (yend)
    plot (xpxl2, ypxl2, rfpart (yend) * xgap)
    plot (xpxl2, ypxl2 + 1, fpart (yend) * xgap)
    
    // main loop
    for x from xpxl1 + 1 to xpxl2 - 1 do
        plot (x, ipart (intery), rfpart (intery))
        plot (x, ipart (intery) + 1, fpart (intery))
        intery = intery + gradient
    end function
    

    【讨论】:

    • +1 表示算法,特别是 Wikipedia 上的链接“小于一个像素长的行应作为特殊情况处理”。如果这篇文章准确地解释了如何处理这种特殊情况,那么这篇文章将更加相关,因为该问题特别提到了亚像素线段作为伪影的可能原因。我希望在 Java2D API 中实现低级渲染算法。我正在寻找的解决方案将解释如何使用 Java2D API 来实现连续的加粗线。感谢您提供信息丰富的回复。
    • 另一种可接受的解决方案将解释为什么目前无法做到这一点,并提供比问题中提出的解决方法更好的解决方法(在渲染形状之前进行点细化。)
    猜你喜欢
    • 2023-03-29
    • 2019-09-10
    • 1970-01-01
    • 2023-02-01
    • 2021-12-14
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多