Java 在 Linux 上抚摸子像素坐标时出现 8 个图形故障

Java 8 graphics glitch when stroking sub-pixel coordinates on Linux

在 Java 8.

中,子像素坐标上的描边似乎被破坏了

我有三组案例,如截图所示(列代表案例,行代表不同的笔划宽度):

Java 7u51(400%比例)

Java8u60(400%比例)

  1. 在同一坐标上填充和描边。按预期工作,描边区域大于填充区域。
  2. 描边被缩小(按描边宽度)并居中(按宽度的一半)以位于填充区域的边界内。这部分在 Java 8 中被破坏为 1px 笔划,其中绘画发生在子像素坐标(第一行)上; 3px stroke 没有这个问题(第三行)。看起来 0.5 为 1px 笔划四舍五入。
  3. 填充矩形以与案例 2 相同的方式缩小并居中。我需要在支持子像素绘图的图形上使用它,以便在单元格重叠时进行非重叠填充。在这里你可以看到填充操作向下舍入0.5到0,所以它只是描边问题。

代码如下:

import static java.awt.BasicStroke.*;

import java.awt.*;
import java.awt.geom.*;

import javax.swing.*;

public class TestCase
{
    public static void main(String[] args)
    {
        JFrame frame = new JFrame("Test case");
        frame.setSize(115, 115);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        frame.getContentPane().add(new TestPanel());

        frame.setVisible(true);
    }

    private static class TestPanel extends JPanel
    {
        TestPanel()
        {
            setOpaque(true);
        }

        @Override
        protected void paintComponent(Graphics g)
        {
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.white);
            g2.fill(getBounds());

            Rectangle2D rect = new Rectangle2D.Double();
            Color background = new Color(0, 255, 255);
            Color border = new Color(255, 0, 0, 128);
            Stroke STROKE_1PX = new BasicStroke(1, CAP_SQUARE, JOIN_MITER);
            Stroke STROKE_2PX = new BasicStroke(2, CAP_SQUARE, JOIN_MITER);
            Stroke STROKE_3PX = new BasicStroke(3, CAP_SQUARE, JOIN_MITER);
            g2.translate(10, 10);

            /**
             * Filling and stroking by original coordinates
             */
            rect.setRect(0, 0, 25, 25);
            g2.setColor(background);
            g2.fill(rect);
            g2.setColor(border);
            g2.setStroke(STROKE_1PX);
            g2.draw(rect);
            g2.translate(0, 35);
            g2.setColor(background);
            g2.fill(rect);
            g2.setColor(border);
            g2.setStroke(STROKE_2PX);
            g2.draw(rect);
            g2.translate(0, 35);
            g2.setColor(background);
            g2.fill(rect);
            g2.setColor(border);
            g2.setStroke(STROKE_3PX);
            g2.draw(rect);

            /**
             * Stroking is shrunk to be inside the filling rect
             */
            g2.translate(35, -70);
            rect.setRect(0, 0, 25, 25);
            g2.setColor(background);
            g2.fill(rect);
            rect.setRect(0.5, 0.5, 24, 24);
            g2.setColor(border);
            g2.setStroke(STROKE_1PX);
            g2.draw(rect);
            g2.translate(0, 35);
            rect.setRect(0, 0, 25, 25);
            g2.setColor(background);
            g2.fill(rect);
            rect.setRect(1, 1, 23, 23);
            g2.setColor(border);
            g2.setStroke(STROKE_2PX);
            g2.draw(rect);
            g2.translate(0, 35);
            rect.setRect(0, 0, 25, 25);
            g2.setColor(background);
            g2.fill(rect);
            rect.setRect(1.5, 1.5, 22, 22);
            g2.setColor(border);
            g2.setStroke(STROKE_3PX);
            g2.draw(rect);

            /**
             * Filling rect is additionally shrunk and centered
             */
            g2.translate(35, -70);
            rect.setRect(0.5, 0.5, 24, 24);
            g2.setColor(background);
            g2.fill(rect);
            g2.setColor(border);
            g2.setStroke(STROKE_1PX);
            g2.draw(rect);
            g2.translate(0, 35);
            rect.setRect(1, 1, 23, 23);
            g2.setColor(background);
            g2.fill(rect);
            g2.setColor(border);
            g2.setStroke(STROKE_2PX);
            g2.draw(rect);
            g2.translate(0, 35);
            rect.setRect(1.5, 1.5, 22, 22);
            g2.setColor(background);
            g2.fill(rect);
            g2.setColor(border);
            g2.setStroke(STROKE_3PX);
            g2.draw(rect);
        }
    }
}

据我测试,Java 7 没有这个问题(在 7u51 上试过),Windows (8u77) 和 Mac (8u60) 也没有。在不同的机器上尝试 Ubuntu(8u60 和 8u77)和 Linux Mint(8u60),错误就在这里。

有人遇到过这样的问题吗?有什么通用的解决方法吗?

我不能在使用描边的地方简单地处理 1px 的大小写。这是因为有很多地方,而且我正在使用不同的 Graphics2D 实现,而且从我使用的内容来看,问题似乎只在 SunGraphics2D 上重现。这意味着我需要在这些地方使用 instanceOf 以不破坏通用逻辑。

我建议您扩展 Graphics2D 并在需要的地方添加纠正措施。

这样您就可以保持主要流程的一致性,并添加所有其他 "logic" 错误可以在需要处理的地方处理。

这为您节省了修改 N 个文件和撤消这些更改(如果它得到修复)的开销,并将逻辑保持在应有的位置。

来自 bug report 讨论:

It is an xrender bug -Dsun.java2d.xrender=false cures it.

我自己没有检查过这个解决方案,因为除了它已被审查之外,我没有收到任何来自错误报告系统的通知。由于这是 Linux-only 问题,因此决定等待官方修复,因为我们的客户中没有那么多 Linux 用户。