Подтвердить что ты не робот

Java 8 графический сбой при поглаживании субпиксельных координат в Linux

Кажется, что поглаживание по подпиксельным координатам стало нарушено в Java 8.

У меня есть три набора случаев, показанных на скриншотах (столбцы представляют случаи, строки представляют разные ширины штрихов):

Java 7u51 (масштаб 400%)
Screenshot on Java 7u51 (400% scale)
Java 8u60 (шкала 400%)
Screenshot on Java 8u60 (400% scale)

  • Заполните и обведите по тем же координатам. Работает по назначению, поглаженная область больше, чем площадь заполнения.
  • Покрытие сокращается (по ширине штриха) и центрируется (на половину ширины) внутри границ области заполнения. Эта часть разбита на Java 8 для 1px-инсульта, где происходит рисование по субпиксельной координате (первая строка); 3px-ход не имеет этой проблемы (третья строка). Кажется, что 0,5 округляется до 1px-хода.
  • Фиксирующий прямоугольник сжимается по центру так же, как и в случае 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 в этих местах, чтобы не нарушать общую логику.

4b9b3361

Ответ 1

От отчет об ошибках обсуждение:

Это ошибка xrender -Dsun.java2d.xrender = false, исцеляет ее.

Я не проверял это решение самостоятельно, так как я не получал никаких уведомлений от системы отчетов об ошибках, кроме того, что он был рассмотрен. И поскольку это была проблема только с Linux, было решено ждать официальных исправлений, поскольку среди наших клиентов было не так много пользователей Linux.

Ответ 2

Я предлагаю вам расширить Graphics2D и добавлять туда коррекционные меры там, где это необходимо.

Таким образом, вы можете сохранить свой основной поток согласованным и добавить все другие "логические" ошибки, которые можно обрабатывать там, где их нужно обрабатывать.

Это экономит ваши накладные расходы на изменение N файлов и отменяет эти изменения, если они исправлены, и сохраняя логику там, где она должна быть.