Качайте Java2D на Java 11 и современном iMac

#java #macos #java-11 #retina-display #java-2d

Вопрос:

Некоторое время назад я написал и работал над панелью масштабирования для поворота мыши, которая обрабатывала подсветку, панорамирование, масштабирование мыши, выделение и т. Д. Это было очень мило.

Я пошел поиграть с ним еще немного сегодня, и он не работал. Я был сбит с толку. Я знал, что у меня где-то есть рабочий пример. Но, прочесывая мой диск, ни один из моих экспериментов не сработал. Я снова начал пытаться заставить его работать.

В конце концов я выяснил, в чем проблема. Это какая-то комбинация JDK 11 и моего нового iMac. Последний раз, когда я работал над этим, был на моем старом Mac (где я мог или не мог использовать JDK 11, я не помню).

Вот как выглядит мое приложение с JDK 11 введите описание изображения здесь

Вот как это выглядит с JDK 8 (вот как это должно выглядеть) введите описание изображения здесь

Если вы поиграете с кодом, вы увидите, что, по-видимому, в JDK 11 происходит какое-то (2X?) масштабирование, но не в JDK 8. Я не знаю, пытается ли он компенсировать большой дисплей на моей машине или что происходит.

Но вы пытаетесь масштабировать или панорамировать под JDK 11 и видите, что он не остается в центре мыши, и как неправильно выделено отслеживание и т. Д.

Как я могу заставить это работать должным образом в JDK 11? Это только для Mac? Или Mac с «мониторами размером с кинотеатр»?

Ниже приведен код:

 import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class TestPanel {

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI() {
        JFrame f = new JFrame("Test Zoom");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new BorderLayout());
        TestPanelZoom p = new TestPanelZoom();
        f.add(p, BorderLayout.CENTER);
        f.setPreferredSize(new Dimension(400, 500));
        f.pack();
        f.setVisible(true);
    }

    private static class TestPanelZoom extends JPanel {

        boolean hilighted = false;
        private int hiliteX = -1;
        private int hiliteY = -1;
        boolean selected = false;
        private int selectX = -1;
        private int selectY = -1;

        private AffineTransform at = new AffineTransform();

        public TestPanelZoom() {
            setBackground(Color.WHITE);
            setForeground(Color.BLACK);
            addMouseAdapter();
            at.setToIdentity();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 500);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setTransform(at);
            paintPanel(g2d);
            g2d.dispose();
        }

        private void paintPanel(Graphics2D g2) {
            g2.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(
                    RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            Color c = g2.getColor();
            g2.setColor(Color.WHITE);
            g2.fill(getBounds());
            g2.setColor(c);

            for (int i = 0; i < 400; i  = 50) {
                Line2D line = new Line2D.Double(i, 0, i, 350);
                g2.draw(line);
                line = new Line2D.Double(0, i, 350, i);
                g2.draw(line);
            }
            if (hilighted) {
                Rectangle2D rect = new Rectangle2D.Double(hiliteX * 50   5, hiliteY * 50   5, 40, 40);
                g2.setColor(Color.GREEN);
                g2.fill(rect);
            }
            if (selected) {
                Rectangle2D rect = new Rectangle2D.Double(selectX * 50   10, selectY * 50   10, 30, 30);
                g2.setColor(Color.RED);
                g2.fill(rect);
            }
        }

        private void addMouseAdapter() {
            MouseAdapter ma = new MouseAdapter() {
                int lx, ly;

                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    AffineTransform t = new AffineTransform();
                    double delta = 1   0.05f * e.getPreciseWheelRotation();
                    int x = e.getX();
                    int y = e.getY();
                    t.translate(x, y);
                    t.scale(delta, delta);
                    t.translate(-x, -y);
                    t.concatenate(at);
                    at = t;
                    revalidate();
                    repaint();
                }

                @Override
                public void mousePressed(MouseEvent e) {
                    lx = e.getX();
                    ly = e.getY();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    update(e);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    update(e);
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    Point2D srcPt = getSrcPoint(e);
                    selected = true;
                    selectX = (int) (srcPt.getX() / 50);
                    selectY = (int) (srcPt.getY() / 50);
                    revalidate();
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    Point2D srcPt = getSrcPoint(e);
                    hilighted = true;
                    hiliteX = (int) (srcPt.getX() / 50);
                    hiliteY = (int) (srcPt.getY() / 50);
                    revalidate();
                    repaint();
                }

                public void update(MouseEvent e) {
                    Point2D srcPt = new Point2D.Double(e.getX(), e.getY());
                    Point2D lastPt = new Point2D.Double(lx, ly);
                    try {
                        at.inverseTransform(srcPt, srcPt);
                        at.inverseTransform(lastPt, lastPt);
                    } catch (NoninvertibleTransformException noninvertibleTransformException) {
                        throw new RuntimeException(noninvertibleTransformException);
                    }
                    double dx = srcPt.getX() - lastPt.getX();
                    double dy = srcPt.getY() - lastPt.getY();
                    at.translate(dx, dy);
                    lx = e.getX();
                    ly = e.getY();
                    revalidate();
                    repaint();
                }

                public Point2D getSrcPoint(MouseEvent e) {
                    Point2D srcPt = new Point2D.Double(e.getX(), e.getY());
                    try {
                        at.inverseTransform(srcPt, srcPt);
                    } catch (NoninvertibleTransformException ex) {
                        throw new RuntimeException(ex);
                    }
                    return srcPt;
                }

            };
            addMouseListener(ma);
            addMouseMotionListener(ma);
            addMouseWheelListener(ma);
        }

    }
}
 

EDIT:

After some more poking around, for whatever reason under JDK 11, the default transform for the Graphics2D is scaled by 2.

In this code, if you print out the default transform, on JDK 11 (in my environment) you get:

 AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]]
 

On JDK 8, you get the Identity.

 AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
 
         protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            System.out.println(g2d.getTransform());
            g2d.setTransform(at);
            paintPanel(g2d);
            g2d.dispose();
        }
 

I initially force it to identiy by using my own original AffineTransform . On JDK 8, this is a NOP, JDK 11, not so much.

I tried this, where I set the default transform ( at ) to the «default» of the graphics context the first time (using its fundamental world view vs forcing my own). That’s make the grid the proper size, but it’s messing with the cursor locations.

         // I remove setting `at` up above, and setting it to identity also.
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            if (at == null) {
                at = g2d.getTransform();
            }
            g2d.setTransform(at);
            paintPanel(g2d);
            g2d.dispose();
        }
 

Так что это не решение.

Может быть, есть способ заставить это работать, используя преобразование по умолчанию для контекста, но я еще не понял этого. Я должен предположить, что преобразование по умолчанию является плавным, не только от JDK к JDK, но и от машины к машине. Это может даже измениться в ситуации с несколькими мониторами.

Редактировать:

Продолжаю идти дальше.

Итак, окно, созданное и отображаемое на экране, в 2 раза больше, чем я прошу в Java. Это я проверяю, создав скриншот окна.

В JDK 8 механизм, который превращает линию в 100 пикселей на Java в линию в 200 пикселей на дисплее, не показан моему коду.

В JDK 11 можно увидеть, как они пытаются повысить это, напрямую используя AffineTransform.

Аналогично, в JDK 8 координаты мыши масштабируются обратно в систему координат Java. Поэтому, если вы поместите на экран коробку размером 400х400 и переместите мышь из одного угла в другой, даже если размер коробки составляет 800х800 пикселей, мышь будет находиться в диапазоне от 0 до 400.

Проблема в JDK 8 заключается в том, что из-за преобразования координаты экрана больше не совпадают с координатами модели. В то время как вы нарисовали прямоугольник 400х400, на экране было нарисовано 400х400 раз 2 (из-за преобразования). Но координаты мыши не указаны в координатах экрана. Необработанная мышь находится в диапазоне 0-400, а не 0-800. Таким образом, вы больше не можете использовать преобразование графики для преобразования координат мыши обратно в координаты модели. Под одеялом происходят махинации и подтасовки. Просто преобразование не является каноническим. Вот почему моя мышка все портит.

Кроме того, я запустил образец на другом компьютере Mac с дисплеем с более низким разрешением, и никаких проблем вообще нет. Преобразование по умолчанию в JDK 11-это идентификация.

Комментарии:

1. Причина проблемы с вашей мышью заключается в том, что в getSrcPoint вы применяете обратное преобразование к местоположению мыши. Поскольку вы инициализировали преобразование с помощью преобразования экрана Graphics2D, которое преобразует координаты java; в координаты экрана, это дает неправильный результат. Вы действительно пробовали мое точное решение ниже на JDK 11?

2. Да, так и было, большое вам спасибо. Я знал, что на этом основан какой-то порядок, и в ретроспективе это имеет полный смысл. Еще раз спасибо.

Ответ №1:

Я не видел никакой разницы между JDK 8 и 11, но проблема в том, что Graphics2D контекст уже преобразован в соответствии с конфигурацией экрана, поэтому там, где вам нужно g2d.setTransform(at) , вам нужно это сделать g2d.transform(at) . Тогда все будет работать так, как вы ожидаете. (Я работал с JDK 17 в OS X 11.4)

Так paintComponent и должно быть (я также добавил некоторые записи):

 protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            AffineTransform t = g2d.getTransform();
            System.out.println("Base G2D:"   t);
            System.out.println("Ours:"   at);
            g2d.transform(at);
            System.out.println("Final:"   g2d.getTransform());

            paintPanel(g2d);
            g2d.dispose();
        }
 

Я проверил это на OpenJDK Runtime Environment 18.9 (build 11.0.2 9) и. Java(TM) SE Runtime Environment (build 1.8.0_201-b09)

Теперь я также протестировал с 11.0.11:

 $ rm *.class
$ java -version
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-11.0.11 9 (build 11.0.11 9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11 9 (build 11.0.11 9, mixed mode)
$ javac TestPanel.java
$ java TestPanel
Base G2D:AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]]
Ours:AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
Final:AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]]
 

Комментарии:

1. Это не имело никакого видимого значения для Java 11 macOS 11.4. Возможно, проблема была устранена между 11 и 17. Я попытаюсь обновить 11 и посмотрю, имеет ли это какое-либо значение.

2. Установил 11.0.11 и 16.0.1 из AdoptJDK, и ни то, ни другое не имело никакого значения.

3. Итак, вы paintComponent похожи на того, кто был выше, но у вас все еще есть проблема?

4. Нет, я попробовал ваше изменение, оно не имело никакого значения ни в одном из JDK, с которыми я тестировал. В настоящее время он по-прежнему работает только с JDK 8.

5. Очень странно, я добавил ведение журнала преобразований и запустил с 11.0.11. Какие записи вы получаете?

Ответ №2:

Для потомков я хотел опубликовать окончательный код, который работает на случай, если кому-то еще понадобится рабочий пример, а не пытаться сопоставить исходный код с обсуждением.

 package pkg;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class TestPanel {

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                createAndShowGUI();
            }
        });
    }

    public static void createAndShowGUI() {
        JFrame f = new JFrame("Test Zoom");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new BorderLayout());
        TestPanelZoom p = new TestPanelZoom();
        f.add(p, BorderLayout.CENTER);
        f.setPreferredSize(new Dimension(400, 500));
        f.pack();
        f.setVisible(true);
    }

    private static class TestPanelZoom extends JPanel {

        boolean hilighted = false;
        private int hiliteX = -1;
        private int hiliteY = -1;
        boolean selected = false;
        private int selectX = -1;
        private int selectY = -1;

        private AffineTransform at = new AffineTransform();

        public TestPanelZoom() {
            setBackground(Color.WHITE);
            setForeground(Color.BLACK);
            addMouseAdapter();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 500);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.transform(at);
            paintPanel(g2d);
            g2d.dispose();
        }

        private void paintPanel(Graphics2D g2) {
            g2.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(
                    RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            Color c = g2.getColor();
            g2.setColor(Color.WHITE);
            g2.fill(getBounds());
            g2.setColor(c);

            for (int i = 0; i < 400; i  = 50) {
                Line2D line = new Line2D.Double(i, 0, i, 350);
                g2.draw(line);
                line = new Line2D.Double(0, i, 350, i);
                g2.draw(line);
            }
            if (hilighted) {
                Rectangle2D rect = new Rectangle2D.Double(hiliteX * 50   5, hiliteY * 50   5, 40, 40);
                g2.setColor(Color.GREEN);
                g2.fill(rect);
            }
            if (selected) {
                Rectangle2D rect = new Rectangle2D.Double(selectX * 50   10, selectY * 50   10, 30, 30);
                g2.setColor(Color.RED);
                g2.fill(rect);
            }
        }

        private void addMouseAdapter() {
            MouseAdapter ma = new MouseAdapter() {
                int lx, ly;

                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    AffineTransform t = new AffineTransform();
                    double delta = 1   0.05f * e.getPreciseWheelRotation();
                    int x = e.getX();
                    int y = e.getY();
                    t.translate(x, y);
                    t.scale(delta, delta);
                    t.translate(-x, -y);
                    t.concatenate(at);
                    at = t;
                    revalidate();
                    repaint();
                }

                @Override
                public void mousePressed(MouseEvent e) {
                    lx = e.getX();
                    ly = e.getY();
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    update(e);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    update(e);
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    Point2D srcPt = getSrcPoint(e);
                    selected = true;
                    selectX = (int) (srcPt.getX() / 50);
                    selectY = (int) (srcPt.getY() / 50);
                    revalidate();
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    Point2D srcPt = getSrcPoint(e);
                    hilighted = true;
                    hiliteX = (int) (srcPt.getX() / 50);
                    hiliteY = (int) (srcPt.getY() / 50);
                    revalidate();
                    repaint();
                }

                public void update(MouseEvent e) {
                    Point2D srcPt = new Point2D.Double(e.getX(), e.getY());
                    Point2D lastPt = new Point2D.Double(lx, ly);
                    try {
                        at.inverseTransform(srcPt, srcPt);
                        at.inverseTransform(lastPt, lastPt);
                    } catch (NoninvertibleTransformException noninvertibleTransformException) {
                        throw new RuntimeException(noninvertibleTransformException);
                    }
                    double dx = srcPt.getX() - lastPt.getX();
                    double dy = srcPt.getY() - lastPt.getY();
                    at.translate(dx, dy);
                    lx = e.getX();
                    ly = e.getY();
                    revalidate();
                    repaint();
                }

                public Point2D getSrcPoint(MouseEvent e) {
                    Point2D srcPt = new Point2D.Double(e.getX(), e.getY());
                    try {
                        at.inverseTransform(srcPt, srcPt);
                    } catch (NoninvertibleTransformException ex) {
                        throw new RuntimeException(ex);
                    }
                    return srcPt;
                }

            };
            addMouseListener(ma);
            addMouseMotionListener(ma);
            addMouseWheelListener(ma);
        }
    }
}