【问题标题】:Java Swing BufferedImage poor qualityJava Swing BufferedImage 质量差
【发布时间】:2015-10-02 07:29:02
【问题描述】:

我正在尝试在 BufferedImage 上创建绘图,然后将其复制到 JPanel。 当我直接在 JPanel 上绘制时,图片的质量是 v.good,但是当使用中间 BufferedImage 时,质量/分辨率明显降低。 我已经使用 OSX 辅助功能面板中的缩放选项进行了检查。 我正在 MacBook Pro Retina 上进行开发。

是否正在发生某种自动扩展? 我对 BufferedImage 做错了什么?

这是演示问题的代码

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();
        }
    }
}
}

编辑: 添加了 4 像素点和 50D 放大的图像。 丑陋的一个来自 BufferedImage 复制到屏幕的 Graphics

【问题讨论】:

  • 对我来说看起来一模一样。可以发截图(可以放大)吗?
  • 很可能是从图像中获取的Graphics 对象使用的渲染提示与面板中使用的不同。
  • 尝试创建两倍大小的BufferedImage(即size.width * 2, size.height * 2),然后将ig 的比例设置为2,将g2draw 的比例设置为0.5
  • 感谢 haraldK!就是这样!我尝试了类似的方法,但在拼命寻找解决方案的过程中,我显然做错了。非常感谢!

标签: java swing bufferedimage


【解决方案1】:

我修正了你的绘图代码。

这是丑陋的 GUI。

  1. 我将面板的大小移动到面板构造函数。设置帧大小包括边框。设置面板大小可为您提供所需的绘图区域。

  2. 我将黑色背景绘画移至paintComponent 方法。您不妨在一个地方完成所有绘画。

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

  4. 我把圆圈放大了,这样你就可以看到清晰度了。我把原点移到圆心,把 DOT_SIZE 变成了半径。

这是代码。

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);
        }
    }

}

【讨论】:

  • 吉尔伯特,感谢您的回答,但您的更改纯属修饰,并不能解决我放大时边缘不均匀的问题。查看图片
【解决方案2】:

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

【讨论】:

【解决方案3】:

我认为图像和面板在 OS X 上针对您未明确设置的提示使用了不同的渲染提示。将此代码的文本输出复制/粘贴回问题中。

import java.awt.*;
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 = 40;

    public static void main(String[] args) {
        JFrame frame = new JFrame("ChartPanel demo");
        frame.setLayout(new GridLayout(0, 1));

        frame.getContentPane().add(new DrawingUgly());
        frame.getContentPane().add(new DrawingOK());
        frame.setSize(new Dimension(400, 300));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
    }

    private static class DrawingOK extends JPanel {

        private static final long serialVersionUID = 1L;

        DrawingOK() {
            setBackground(Color.GREEN);
        }

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

            Graphics2D g2draw = (Graphics2D) g.create();
            System.out.println("Panel Rendering Hints:");
            printRenderingHints(g2draw);
            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;

        DrawingUgly() {
            setBackground(Color.RED);
        }

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

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

            Graphics2D ig = image.createGraphics();

            System.out.println("Image Rendering Hints:");
            printRenderingHints(ig);
            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();
            }
        }
    }

    private static void printRenderingHints(Graphics2D g) {
        RenderingHints renderingHints = g.getRenderingHints();
        RenderingHints.Key[] renderHintsKeys = {
            RenderingHints.KEY_ALPHA_INTERPOLATION,
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.KEY_COLOR_RENDERING,
            RenderingHints.KEY_DITHERING,
            RenderingHints.KEY_FRACTIONALMETRICS,
            RenderingHints.KEY_INTERPOLATION,
            RenderingHints.KEY_RENDERING,
            RenderingHints.KEY_STROKE_CONTROL,
            RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.KEY_TEXT_LCD_CONTRAST
        };
        for (RenderingHints.Key key : renderHintsKeys) {
            Object o = renderingHints.get(key);
            String value = o==null ? "null" : o.toString();
            System.out.println(key + " \t" + value);
        }
    }
}

请注意,在 Windows 上,它会生成相同的值列表。

【讨论】:

  • 嗨 Andrew 这是我检查的第一件事。在我的 Macbook Retina 上,您的代码也会打印相同的值。这更有可能与 Retina 显示屏的高分辨率有关。
  • "..code prints: .." ..what?将输出编辑到问题中。
  • 无法粘贴,文字太多Panel Rendering Hints: Alpha blending interpolation method key null Global antialiasing enable key Nonantialiased rendering mode Color rendering quality key null Dithering quality key null Fractional metrics enable key Integer text metrics mode Image interpolation method key null Global rendering quality key Default rendering methods Stroke normalization control key Default stroke normalization Text-specific antialiasing enable key Default antialiasing text mode Text-specific LCD contrast key 140 Image Rendering Hints: 一模一样
  • @ivenhov:为了提高可读性,请编辑您的问题以包含这些结果。
  • @AndrewThompson:在 Mac OS X 10.9 和 10.10 上相同,都 sans 视网膜显示。 @
【解决方案4】:

HaraldK 在下面的一个问题中给出了非常好的建议。 BufferedImage 大小需要乘以 2,该图像的 Graphics2D 必须设置为比例 2,目标 Graphics2D(屏幕设备)需要缩放 0.5。 使用这些设置,两个圆圈在放大时看起来完全相同。 下面是完整的、修改后的 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);
    }
}

【讨论】:

  • 你不能可靠地假设缩放因子是 2。
  • 没有。但是你可以通过反射来获取它GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); final GraphicsDevice device = env.getDefaultScreenDevice(); Field field = device.getClass().getDeclaredField("scale");
  • 这看起来也不是特别便携。而且我认为即使这样,文本也不能很好地呈现。不幸的答案是,在 Java 获得 HDPI api 之前没有好的方法可以做到这一点
猜你喜欢
  • 2020-10-12
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-05
  • 2020-10-08
  • 2018-12-04
相关资源
最近更新 更多