完美呈现的图形在Java Swing/AWT中

huangapple 未分类评论48阅读模式
英文:

Pixel perfect graphics in Java swing/awt

问题

以下是你提供的内容的翻译:

在Swing/AWT组件中有实现像素完美图形的方法吗?

我正在通过扩展AbstractBorder为按钮实现自定义边框。为了演示目的,厚度总是等于3,每条线都使用不同的颜色绘制。

import javax.swing.*;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicLookAndFeel;
import java.awt.*;

public class ExampleFrame extends JFrame {

    public ExampleFrame() {
        init();
    }

    public static void main(String[] args) throws UnsupportedLookAndFeelException 
    {
        UIManager.setLookAndFeel(new CustomLookAndFeel());

        ExampleFrame frame = new ExampleFrame();
        frame.setVisible(true);
    }

    private void init() {
        setLayout(new BorderLayout());

        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        getContentPane().add(panel, BorderLayout.NORTH);

        GridBagConstraints constraints = new GridBagConstraints();

        JButton button1 = new JButton("aaa");
        button1.setVerticalTextPosition(JButton.BOTTOM);
        button1.setHorizontalTextPosition(JButton.CENTER);
        button1.setFocusable(false);

        constraints.insets = new Insets(2, 2, 2, 2);
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.gridwidth = 1;
        constraints.gridheight = 3;
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button1, constraints);

        JButton button2 = new JButton("bbb");
        button2.setHorizontalAlignment(JButton.CENTER);
        button2.setFocusable(false);

        constraints.gridx = 1;
        constraints.gridy = 0;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button2, constraints);

        JButton button3 = new JButton("eee");
        button3.setHorizontalAlignment(JButton.CENTER);
        button3.setFocusable(false);

        constraints.gridx = 1;
        constraints.gridy = 1;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button3, constraints);

        JButton button4 = new JButton("ddd");
        button4.setHorizontalAlignment(JButton.CENTER);
        button4.setFocusable(false);

        constraints.gridx = 1;
        constraints.gridy = 2;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button4, constraints);

        pack();
    }

    private static final class CustomBorder extends AbstractBorder {

        private final int thickness;

        public CustomBorder() {
            this(1);
        }

        public CustomBorder(int thickness) {
            this.thickness = thickness;
        }

        @Override
        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            //            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
            ((Graphics2D) g).setStroke(new BasicStroke(1.0f));

            for (int i = 0; i < thickness; i++) {
                switch (i) {
                    case 0: {
                        g.setColor(Color.RED);
                    }
                    break;
                    case 1: {
                        g.setColor(Color.GREEN);
                    }
                    break;
                    case 2: {
                        g.setColor(Color.BLUE);
                    }
                    break;
                }

                // top-left -> top-right
                g.drawLine(x, y + i, x + width, y + i);
                // top-left > bottom-left
                g.drawLine(x + i, y, x + i, y + height);
            }
        }

        @Override
        public Insets getBorderInsets(Component c, Insets insets) {
            insets.top = thickness;
            insets.bottom = thickness;
            insets.left = thickness;
            insets.right = thickness;
            return insets;
        }
    }

    private static final class CustomLookAndFeel extends BasicLookAndFeel {

        @Override
        protected void initComponentDefaults(UIDefaults table) {
            super.initComponentDefaults(table);

            // button
            Border buttonBorder = new CustomBorder(3);
            table.put("Button.border", buttonBorder);
            table.put("Button.background", new Color(150, 150, 182));
            table.put("Button.foreground", Color.BLACK);
        }

        @Override
        public String getName() {
            return "custom";
        }

        @Override
        public String getID() {
            return "custom";
        }

        @Override
        public String getDescription() {
            return "custom";
        }

        @Override
        public boolean isNativeLookAndFeel() {
            return false;
        }

        @Override
        public boolean isSupportedLookAndFeel() {
            return true;
        }
    }
}

即使所有3个按钮具有相同的边框参数,我得到了不同的边框宽度和颜色显示。

VALUE_STROKE_PURE和VALUE_STROKE_NORMALIZE提示会产生不同的结果,但它们都不是像素精确的。

按钮“bbb”,“eee”和“ddd”在右侧具有相同的宽度和高度,但边框的颜色和总宽度仍然不同。此外,正在使用的默认笔画的宽度为1.0f。

我认为这是因为Java 2D几何图形使用浮点数运算。有没有办法克服这个限制?

我尝试了不同的例程,如drawRect和不同的抗锯齿提示,但都没有成功。

英文:

Is there a way to achieve pixel perfect graphics in Swing/AWT components?

I am implementing a custom border for a button by extending AbstractBorder. For demonstration purpose thickness is always equal to 3 and each line is painted using different color.

import javax.swing.*;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicLookAndFeel;
import java.awt.*;

public class ExampleFrame extends JFrame {

    public ExampleFrame() {
        init();
    }

    public static void main(String[] args) throws UnsupportedLookAndFeelException 
    {
        UIManager.setLookAndFeel(new CustomLookAndFeel());

        ExampleFrame frame = new ExampleFrame();
        frame.setVisible(true);
    }

    private void init() {
        setLayout(new BorderLayout());

        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        getContentPane().add(panel, BorderLayout.NORTH);

        GridBagConstraints constraints = new GridBagConstraints();

        JButton button1 = new JButton(&quot;aaa&quot;);
        button1.setVerticalTextPosition(JButton.BOTTOM);
        button1.setHorizontalTextPosition(JButton.CENTER);
        button1.setFocusable(false);

        constraints.insets = new Insets(2, 2, 2, 2);
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.gridwidth = 1;
        constraints.gridheight = 3;
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button1, constraints);

        JButton button2 = new JButton(&quot;bbb&quot;);
        button2.setHorizontalAlignment(JButton.CENTER);
        button2.setFocusable(false);

        constraints.gridx = 1;
        constraints.gridy = 0;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.fill = GridBagConstraints.BOTH;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button2, constraints);

        JButton button3 = new JButton(&quot;eee&quot;);
        button3.setHorizontalAlignment(JButton.CENTER);
        button3.setFocusable(false);

        constraints.gridx = 1;
        constraints.gridy = 1;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button3, constraints);

        JButton button4 = new JButton(&quot;ddd&quot;);
        button4.setHorizontalAlignment(JButton.CENTER);
        button4.setFocusable(false);

        constraints.gridx = 1;
        constraints.gridy = 2;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.anchor = GridBagConstraints.LINE_START;
        panel.add(button4, constraints);

        pack();
    }

    private static final class CustomBorder extends AbstractBorder {

        private final int thickness;

        public CustomBorder() {
            this(1);
        }

        public CustomBorder(int thickness) {
            this.thickness = thickness;
        }

        @Override
        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            //            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
            ((Graphics2D) g).setStroke(new BasicStroke(1.0f));

            for (int i = 0; i &lt; thickness; i++) {
                switch (i) {
                    case 0: {
                        g.setColor(Color.RED);
                    }
                    break;
                    case 1: {
                        g.setColor(Color.GREEN);
                    }
                    break;
                    case 2: {
                        g.setColor(Color.BLUE);
                    }
                    break;
                }

                // top-left -&gt; top-right
                g.drawLine(x, y + i, x + width, y + i);
                // top-left &gt; bottom-left
                g.drawLine(x + i, y, x + i, y + height);
            }
        }

        @Override
        public Insets getBorderInsets(Component c, Insets insets) {
            insets.top = thickness;
            insets.bottom = thickness;
            insets.left = thickness;
            insets.right = thickness;
            return insets;
        }
    }

    private static final class CustomLookAndFeel extends BasicLookAndFeel {

        @Override
        protected void initComponentDefaults(UIDefaults table) {
            super.initComponentDefaults(table);

            // button
            Border buttonBorder = new CustomBorder(3);
            table.put(&quot;Button.border&quot;, buttonBorder);
            table.put(&quot;Button.background&quot;, new Color(150, 150, 182));
            table.put(&quot;Button.foreground&quot;, Color.BLACK);
        }

        @Override
        public String getName() {
            return &quot;custom&quot;;
        }

        @Override
        public String getID() {
            return &quot;custom&quot;;
        }

        @Override
        public String getDescription() {
            return &quot;custom&quot;;
        }

        @Override
        public boolean isNativeLookAndFeel() {
            return false;
        }

        @Override
        public boolean isSupportedLookAndFeel() {
            return true;
        }
    }
}

Even though all 3 buttons have the same border parameters I get different border width and colors displayed.

VALUE_STROKE_PURE and VALUE_STROKE_NORMALIZE hints give different results, but none of them is pixel accurate.

完美呈现的图形在Java Swing/AWT中
完美呈现的图形在Java Swing/AWT中

Buttons "bbb", "eee" and "ddd" on the right side have the same width and height, but still the colors and total width of the border is different. Additionally, the default stroke that is being used has width of 1.0f.

I assume this happens, because Java 2D geometry operates on floating point numbers. Is there any way to overcome this limitation?

I tried different routines e.g. drawRect and different anti-aliasing hints, but no luck.

huangapple
  • 本文由 发表于 2020年5月29日 20:13:48
  • 转载请务必保留本文链接:https://java.coder-hub.com/62085723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定