Java Swing BufferedImage poor quality

时间:2015-07-13 21:02:56

标签: java swing bufferedimage

I'm trying to create a drawing on BufferedImage and then copy in onto JPanel. When I draw directly on JPanel quality of the picture is v.good but when using intermediate BufferedImage quality / resolution is visibly reduced. I've checked that with zoom option from OSX's Accessibility panel. I'm developing on MacBook Pro Retina.

Is there some sort of automated scaling happening? What am I doing wrong with BufferedImage?

Here's the code demonstrating the problem

package com.sample.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class QualityProblem {

private static final double DOT_SIZE = 4;

public static void main(String[] args) {
    JFrame frame = new JFrame("ChartPanel demo");
    frame.setBackground(Color.WHITE);

    // JPanel draw = new DrawingOK();
    JPanel draw = new DrawingUgly();
    draw.setBackground(Color.BLACK);

    frame.getContentPane().add(draw, BorderLayout.CENTER);
    frame.setSize(new Dimension(1200, 900));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    frame.setVisible(true);
}

private static class DrawingOK extends JPanel {

    private static final long serialVersionUID = 1L;

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

        Graphics2D g2draw = (Graphics2D) g.create();
        try {
            g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2draw.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

            Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
            g2draw.setColor(Color.YELLOW);
            g2draw.fill(e);
        } finally {
            g2draw.dispose();
        }
    }
}

private static class DrawingUgly extends JPanel {

    private static final long serialVersionUID = 1L;

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

        Dimension size = getParent().getSize();
        BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);

        Graphics2D ig = image.createGraphics();
        ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

        Graphics2D g2draw = (Graphics2D) g.create();
        try {
            Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
            ig.setColor(Color.YELLOW);
            ig.fill(e);
            g2draw.drawImage(image, 0, 0, null);
        } finally {
            ig.dispose();
            g2draw.dispose();
        }
    }
}
}

Edited: Added images with 4 pixel dot and 50D both zoomed in. Ugly one comes from BufferedImage copied onto screen's Graphics Small 4pixel dot Big balls

3 个答案:

答案 0 :(得分:1)

我修改了您的绘图代码。

这是丑陋的GUI。

ChartPanel demo

  1. 我将面板的大小调整为面板构造函数。设置框架大小包括边框。设置面板大小可以为您提供所需的绘图区域。

  2. 我将黑色背景绘画移动到paintComponent方法。你不妨在一个地方做所有的绘画。

  3. 我清理了您的绘图代码。您无需复制paintComponent图形实例即可获得Graphics2D。

  4. 我把圆做得更大,所以你可以看到清晰度。我将原点移动到圆的中心,并将DOT_SIZE变为半径。

  5. 这是代码。

    package com.ggl.testing;
    
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.geom.Ellipse2D;
    import java.awt.image.BufferedImage;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class QualityProblem implements Runnable {
    
        private static final double DOT_SIZE = 50D;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new QualityProblem());
        }
    
        @Override
        public void run() {
            JFrame frame = new JFrame("ChartPanel demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setBackground(Color.WHITE);
    
            // JPanel draw = new DrawingOK();
            JPanel draw = new DrawingUgly();
    
            frame.getContentPane().add(draw, BorderLayout.CENTER);
            frame.pack();
            frame.setVisible(true);
        }
    
        private class DrawingOK extends JPanel {
    
            private static final long serialVersionUID = 1L;
    
            public DrawingOK() {
                this.setPreferredSize(new Dimension(600, 400));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                Graphics2D g2draw = (Graphics2D) g;
    
                g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                g2draw.setRenderingHint(RenderingHints.KEY_RENDERING,
                        RenderingHints.VALUE_RENDER_QUALITY);
                g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                        RenderingHints.VALUE_STROKE_PURE);
    
                g2draw.setColor(Color.BLACK);
                g2draw.fillRect(0, 0, getWidth(), getHeight());
    
                Ellipse2D.Double e = new Ellipse2D.Double(300D - DOT_SIZE,
                        200D - DOT_SIZE, DOT_SIZE + DOT_SIZE, DOT_SIZE + DOT_SIZE);
                g2draw.setColor(Color.YELLOW);
                g2draw.fill(e);
            }
        }
    
        private class DrawingUgly extends JPanel {
    
            private static final long serialVersionUID = 1L;
    
            public DrawingUgly() {
                this.setPreferredSize(new Dimension(600, 400));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                BufferedImage image = new BufferedImage(getWidth(), getHeight(),
                        BufferedImage.TYPE_INT_RGB);
    
                Graphics2D ig = image.createGraphics();
                ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                ig.setRenderingHint(RenderingHints.KEY_RENDERING,
                        RenderingHints.VALUE_RENDER_QUALITY);
                ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                        RenderingHints.VALUE_STROKE_PURE);
    
                ig.setColor(Color.BLACK);
                ig.fillRect(0, 0, getWidth(), getHeight());
    
                Ellipse2D.Double e = new Ellipse2D.Double(300D - DOT_SIZE,
                        200D - DOT_SIZE, DOT_SIZE + DOT_SIZE, DOT_SIZE + DOT_SIZE);
                ig.setColor(Color.YELLOW);
                ig.fill(e);
                ig.dispose();
    
                g.drawImage(image, 0, 0, this);
            }
        }
    
    }
    

答案 1 :(得分:1)

这很简单,因为在一种情况下,您正在绘制一个硬件支持的表面表示它是640x480,但渲染是在2倍(或显示器的任何缩放因子)分辨率下完成的。在BufferedImage的情况下,您将绘制到文字的640x480像素缓冲区。显然,这看起来会更糟。

答案 2 :(得分:1)

HaraldK在下面的一条评论中提出了非常好的建议。 BufferedImage大小需要乘以2,该图像的Graphics2D必须使用比例2设置,而目标Graphics2D(屏幕设备)必须使用0.5进行缩放。 使用这些设置时,两个圆圈在放大时看起来完全相同。 Bellow完整,修改过的DrawingUgly类。

    private static class DrawingUgly extends JPanel {

    private static final long serialVersionUID = 1L;

    public DrawingUgly() {
        this.setPreferredSize(new Dimension(600, 25));
    }

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

        Graphics2D g2draw = (Graphics2D) g.create();

        double scale = 2;
        BufferedImage image = new BufferedImage((int) (getWidth() * scale), (int) (getHeight() * scale), BufferedImage.TYPE_INT_RGB);

        Graphics2D ig = image.createGraphics();
        ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

        ig.scale(scale, scale);

        ig.setColor(Color.BLACK);
        ig.fillRect(0, 0, getWidth(), getHeight());

        Ellipse2D.Double e = new Ellipse2D.Double(10, 10, DOT_SIZE, DOT_SIZE);
        ig.setColor(Color.YELLOW);
        ig.fill(e);
        ig.dispose();

        g2draw.scale(1.0d / scale, 1.0d / scale);

        g2draw.drawImage(image, 0, 0, this);
    }
}