【问题标题】:Trying to paint image to JFrame with Java BufferedImage, Graphics尝试使用 Java BufferedImage、Graphics 将图像绘制到 JFrame
【发布时间】:2012-04-03 06:48:36
【问题描述】:

我正在尝试捕获屏幕,然后在缩放图像时递归地将图像绘制到 JFrame(以创建当您在镜子中看镜子时获得的效果)。

我的代码有问题 - 它没有绘制任何图形。我做错了什么?

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;


public class ScreenCapture extends JFrame {

    BufferedImage screenCapture;
    Graphics screenCaptureGraphics;
    private static int recurseCount = 0;
    private static float $scale = 0.9f;
    private static float scale = 1.0f;
    private static int height;
    private static int width;

    ScreenCapture() {
        try {
            screenCapture = new Robot().createScreenCapture(
                       new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );
            height = screenCapture.getHeight();
            width = screenCapture.getWidth();
            setSize(new Dimension(width, height));
            addWindowListener(new LocalWindowListener());
            Graphics g = recursiveDraw(screenCapture, getGraphics());
            paint(g);
        } catch (HeadlessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (AWTException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private Graphics recursiveDraw(BufferedImage img, Graphics imgG) {
        updateScale(++recurseCount);
        float newWidth = scale*width;
        float newHeight = scale*height;
        int w = (int) newWidth;
        int h = (int) newHeight;
        System.out.println("W: " + w + "; H: " + h);
        if (w >= 10 && h >= 10) {
            //scale image
            System.out.print("Creating scaled Image...");
            Image scaled = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
            BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            imgG = resized.createGraphics();
            imgG.drawImage(scaled, 0, 0, null);
            System.out.println("...Image drawn to graphics");
            //return new graphics
            return recursiveDraw(resized, imgG);
        } else {
            //otherwise return old graphics
            System.out.println("Completed.");
            return imgG;
        }
    }


    private void updateScale(int count) {
        for (int i=0; i<count; i++) {
            scale *= $scale;
        }
        System.out.println("Updated scale: " + scale + "; Recurse count: " + recurseCount);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ScreenCapture().setVisible(true);
            }
        });
    }

    private class LocalWindowListener extends WindowAdapter {
        @Override
        public void windowClosing(WindowEvent e) {
            System.exit(0); return;
        }
    }

}

编辑: 这是我在@andrew-thompson 的回答之后尝试的:

ScreenCapture() {
    try {
        screenCapture = new Robot().createScreenCapture(
                   new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );
        height = screenCapture.getHeight();
        width = screenCapture.getWidth();
        setSize(new Dimension(width, height));
        addWindowListener(new LocalWindowListener());
        setLayout(new GridLayout());
        add(new PaintPanel());
    } catch (HeadlessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (AWTException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

private class PaintPanel extends JPanel {
    @Override
    public void paintComponent(Graphics g) {
        g=recursiveDraw(screenCapture, g);
        //what to do with g?
    }
}

我仍然有同样的问题,我不知道如何将 BufferedImage 绘制到图形上。

【问题讨论】:

  • LocalWindowListener - 没必要。只需拨打JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
  • 哦,我在 EXIT_ON_CLOSE 也不起作用之前添加了这个;因为我的 updateScale 循环一直存在问题。修复循环后,我忘了改回来。

标签: java swing graphics bufferedimage awtrobot


【解决方案1】:

会将您的 Swing 代码与递归图像创建代码分开。事实上,考虑创建一个静态方法,该方法创建并返回 BufferedImage 并且其中没有 Swing 代码。然后让您的 GUI 在需要时调用该方法,并获取图像并将其写入磁盘或将其显示在 JLabel 的 ImageIcon 中。

当我这样做时(实际上是今天),我用这个签名创建了一个递归方法

private static void recursiveDraw(BufferedImage img, Graphics imgG, double scale) { 

并使用此方法体(在伪代码中)

start recursiveDraw method 
   // most important: all recursions must have a good ending condition: 
   get img height and width. If either <= a min, return 
   create a BufferedImage, smlImg, for the smaller image using the height, 
        width and scale factor 
   Get the Graphics object, smlG, from the small image 
   Use smlG.drawImage(...) overload to draw the big image in shrunken 
        form onto the little image 
   recursively call recursiveDraw passing in smlImg, smlG, and scale. 
   dispose of smlG 
   draw smlImg (the small image) onto the bigger one using the bigger's 
        Graphics object (passed into this method) and a different 
        overload of the drawImage method. 
end recursiveDraw method

该算法生成的图像如下:

例如:

import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class RecursiveDrawTest {
   private static final Color BACKGRND_1 = Color.green;
   private static final Color BACKGRND_2 = Color.MAGENTA;
   private static final Color FOREGRND_1 = Color.blue;
   private static final Color FOREGRND_2 = Color.RED;

   private static void createAndShowGui() {
      final JPanel mainPanel = new JPanel(new BorderLayout());
      final JSlider slider = new JSlider(50, 90, 65);
      slider.setMajorTickSpacing(10);
      slider.setMinorTickSpacing(5);
      slider.setPaintLabels(true);
      slider.setPaintTicks(true);

      JPanel southPanel = new JPanel();
      southPanel.add(new JLabel("Percent Size Reduction:"));
      southPanel.add(slider);
      southPanel.add(new JButton(new AbstractAction("Create Recursive Image") {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            try {
               double scale = slider.getValue() / 100.0;
               BufferedImage img = createRecursiveImg(scale);
               ImageIcon icon = new ImageIcon(img);
               JLabel label = new JLabel(icon);

               Window win = SwingUtilities.getWindowAncestor(mainPanel);
               JDialog dialog = new JDialog(win, "Image", ModalityType.MODELESS);
               dialog.getContentPane().add(label);
               dialog.pack();
               dialog.setLocationRelativeTo(null);
               dialog.setVisible(true);
            } catch (AWTException e) {
               e.printStackTrace();
            }
         }
      }));

      mainPanel.add(new JScrollPane(new JLabel(new ImageIcon(createLabelImg()))), 
            BorderLayout.CENTER);
      mainPanel.add(southPanel, BorderLayout.PAGE_END);

      JFrame frame = new JFrame("RecursiveDrawTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   // create a background image to display in a JLabel so that the GUI
   // won't be boring.
   private static BufferedImage createLabelImg() {
      Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
      int width = (5 * d.width) / 6;
      int height = (5 * d.height) / 6;
      BufferedImage img = new BufferedImage(width, height, 
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = img.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setPaint(new GradientPaint(0, 0, BACKGRND_1, 40, 40, BACKGRND_2, true));
      g2.fillRect(0, 0, width, height);
      g2.setPaint(new GradientPaint(0, height, FOREGRND_1, 40, height - 40, FOREGRND_2, true));
      g2.fillOval(0, 0, 2 * width, 2 * height);
      g2.dispose();
      return img;
   }

   // non-recursive image to get the initial image that will be drawn recursively
   public static BufferedImage createRecursiveImg(double scale) throws AWTException {
      Robot robot = new Robot();
      Dimension screenSz = Toolkit.getDefaultToolkit().getScreenSize();
      Rectangle screenRect = new Rectangle(screenSz);
      BufferedImage img = robot.createScreenCapture(screenRect);
      Graphics g = img.getGraphics();
      recursiveDraw(img, g, scale); // call recursive method
      g.dispose();
      return img;
   }

   // recursive method to draw image inside of image
   private static void recursiveDraw(BufferedImage img, Graphics g, double scale) {
      int w = img.getWidth();
      int h = img.getHeight();

      int smlW = (int)(w * scale);
      int smlH = (int)(h * scale);
      // criteria to end recursion
      if (smlW <= 1 || smlH <= 1) {
         return; 
      }

      BufferedImage smlImg = new BufferedImage(smlW, smlH, BufferedImage.TYPE_INT_ARGB);
      Graphics smlG = smlImg.getGraphics();
      // draw big image in little image, scaled to little image
      smlG.drawImage(img, 0, 0, smlW, smlH, null);

      // recursive call
      recursiveDraw(smlImg, smlG, scale);
      smlG.dispose(); // clear resources when done with them

      // these guys center the smaller img on the bigger
      int smlX = (w - smlW) / 2;
      int smlY = (h - smlH) / 2;
      // draw small img on big img
      g.drawImage(smlImg, smlX, smlY, smlW, smlH, null);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

【讨论】:

  • 哈哈,你是 Fubarable。我不想在 java-forums 帖子中发布这个问题,以防有人说我在喂另一个人。
  • 我的程序递归很好,输出字符串告诉我每个递归的新大小(它会向下缩放直到接近 10x10 像素)。我在实际绘制图形时遇到了麻烦,尤其是在另一个图形之上。
  • @user1031312:相互叠加的绘制是在 BufferedImage 上完成的,而不是在组件上。完成 BufferedImage 后,您可以将其显示在 JLabel 持有的 ImageIcon 中。
【解决方案2】:
 Graphics g = recursiveDraw(screenCapture, getGraphics());

不要打电话给getGraphics()。覆盖 paint(Graphics)1 并使用提供的 Graphics 实例。

  1. 使用 Swing 时,实际上最好覆盖 JComponentJPanelpaintComponent(Graphics) 方法。然后将其添加到顶级容器中。

【讨论】:

  • 感谢您的回答。我不确定如何实现它 - 请在编辑后检查新代码?我不确定在 paintComponent 方法中使用 Graphics 对象做什么才能使其工作。
【解决方案3】:

这是你所希望的吗:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.Toolkit;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;

import javax.swing.*;

public class CaptureScreen extends JPanel
{
    private BufferedImage screenShot;
    private JLabel imageLabel;
    private BufferedImage secondScreenShot;

    public void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("CAPTURE SCREEN");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);

        //imageLabel = new JLabel();
        //getImageForLabel();
        //add(imageLabel);

        frame.getContentPane().add(this);
        frame.setSize(600, 600);
        frame.setVisible(true);
    }

    private void getImageForLabel()
    {
        Robot robot = null;
        try
        {
            robot = new Robot();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        screenShot = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
        ImageIcon icon = new ImageIcon(screenShot);
        imageLabel.setIcon(icon);
    }

    public void paintComponent(Graphics g)
    {
        Robot robot = null;
        try
        {
            robot = new Robot();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

        screenShot = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
        secondScreenShot = getScaledImage((Image) screenShot, 300, 300);

        g.drawImage(screenShot, 0, 0, null);
        g.drawImage(secondScreenShot, 150, 150, null);
    }

    private BufferedImage getScaledImage(Image srcImg, int w, int h)
    {
        BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TRANSLUCENT);
        Graphics2D g2 = resizedImg.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.drawImage(srcImg, 0, 0, w, h, null);
        g2.dispose();
        return resizedImg;
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new CaptureScreen().createAndDisplayGUI();
            }
        });
    }
}

这是输出:

【讨论】:

  • 我知道如何在 Swing 中显示图像,但我的问题是如何在图形上绘制图形或创建图像并在该图像上绘制较小的图像。我知道还有其他方法可以达到相同的效果,但是您正在远离我原来的问题。我正在学习如何绘画。
【解决方案4】:

摆脱递归。创建一个大小正确的缓冲图像并为其创建一个 Graphics 对象。只需使用循环将逐渐缩小的图像绘制到您选择的任何阈值。最后在 paintComponent() 中使用 g.drawImage() 将图像绘制到屏幕上。如果您保持递归,则需要传递图像并不断覆盖按比例缩小的图像。您不需要从该方法返回任何内容。

【讨论】:

  • Just use a loop to draw progressively smaller scaled images down to whatever threshold you choose 这实际上是我现在的问题。如果我知道这一点,我就不需要其他方法了。我将g.drawImage(scaledImage, 0, 0, null) 移动到paintComponent 中,但它会覆盖旧图像的背景,所以我得到的图像较小但背景为灰色。
猜你喜欢
  • 1970-01-01
  • 2015-06-15
  • 2015-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-04
  • 2012-02-23
相关资源
最近更新 更多