javabufferedimageimage-resizingaffinetransformimgscalr

Dynamically resize a bufferedimage in java


I tried resizing the buffered image using AffineTransform as well as Scalr.resize

Here are my codes for both of them.

using Scalr.resize:

    BufferedImage buff = robot.createScreenCapture(new Rectangle(bufx,bufy,bufwidth,bufheight)); // x-coord, y-coord, width, height

    BufferedImage scrCapt = Scalr.resize(buff, Method.BALANCED, scrwidth, scrheight);

using AffineTransform:

BufferedImage buff = robot.createScreenCapture(new Rectangle(bufx,bufy,bufwidth,bufheight)); // x-coord, y-coord, width, height

BufferedImage scrCapt = new BufferedImage(bufwidth,bufheight,BufferedImage.TYPE_INT_ARGB);
AffineTransform atscr = new AffineTransform();


atscr.scale(aspectRatioWidth,aspectRatioHeight);
AffineTransformOp scaleOp = new AffineTransformOp(atscr, AffineTransformOp.TYPE_BILINEAR);
scrCapt = scaleOp.filter(buff, scrCapt);

the variables have been declared in the beginning inside class:

static int bufx = 0;
static int bufy = 0;
static int bufwidth = 1;
static int bufheight = 1;
static int scrwidth = 0;
static int scrheight = 0;
static float aspectRatioWidth = 0;
static float aspectRatioHeight = 0;

I am getting the values for for all the variables dynamically inside a different method:

aspectRatioWidth = bufwidth/scrwidth;
aspectRatioHeight = bufheight/scrheight;

However when I run this code I get an error in both the functions AffineTransform as well as Scalr.resize:

Scalr.resize:

Exception in thread "Thread-2" java.lang.IllegalArgumentException: Width (0) and height (0) cannot be <= 0
at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1016)
at java.awt.image.BufferedImage.<init>(BufferedImage.java:331)
at org.imgscalr.Scalr.createOptimalImage(Scalr.java:2006)
at org.imgscalr.Scalr.scaleImage(Scalr.java:2133)
at org.imgscalr.Scalr.resize(Scalr.java:1667)
at org.imgscalr.Scalr.resize(Scalr.java:1415)

AffineTransform:

Exception in thread "Thread-2" java.awt.image.ImagingOpException: Unable to invert transform AffineTransform[[0.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
at java.awt.image.AffineTransformOp.validateTransform(AffineTransformOp.java:558)
at java.awt.image.AffineTransformOp.<init>(AffineTransformOp.java:151)

How do I go about this? I understand that this is happening because I am changing the variable in a different method and accessing them in another one. But those two methods can't be combined. Is there any way I can make this work?

EDIT:

I changed the method of resizing Here's what I did now

public static BufferedImage resizeImage(BufferedImage image, double scalewidth, double scaleheight){

    BufferedImage img = new BufferedImage(image.getWidth(), image.getHeight(),BufferedImage.SCALE_FAST);
    Graphics2D g = img.createGraphics();
    g.scale(scalewidth, scaleheight);
    g.drawImage(image, null, 0, 0);
    g.dispose();
    return image;
}

EDIT(2):

For a clearer idea of what is happening exactly:

This is a method which returns scrwidth and scrheight

public static void showOnScreen( int screen, JFrame framenew )
   {
    GraphicsEnvironment ge = GraphicsEnvironment
      .getLocalGraphicsEnvironment();
    GraphicsDevice[] gs = ge.getScreenDevices();

  for (int i = 0; i < gs.length; i++) {
      screenwidth.add(gs[i].getDisplayMode().getWidth());
      screenheight.add(gs[i].getDisplayMode().getHeight());
}

scrwidth = screenwidth.get(screenwidth.size()-1);
scrheight = screenheight.get(screenheight.size()-1);




  System.out.print(ge);
  System.out.print(gs);
  if( screen > -1 && screen < gs.length )
  {gs[screen].setFullScreenWindow( framenew );}
  else if( gs.length > 0 )
  {gs[0].setFullScreenWindow( framenew );}
  else
  {throw new RuntimeException( "No Screens Found" );}}

And this is the actionlistener which returns bufwidth and bufheight:

  btnNewButton.addActionListener(new ActionListener() {      
  public void actionPerformed(ActionEvent e)
  {
      //Execute when button is pressed
      System.out.println("You clicked the button");

      int ind = c.getSelectedIndex();
        bufx = capx.get(ind);
        bufy = capy.get(ind);
        bufwidth = capwidth.get(ind);
        bufheight = capheight.get(ind);
        frame.setVisible(false);
        framenew.setVisible(true);
        showOnScreen(1,framenew);

        aspectRatioWidth = (double) bufwidth/scrwidth;
        aspectRatioHeight = (double) bufheight/scrheight;   

            System.out.print("aspectRatioWidth:  ");
            System.out.println(aspectRatioWidth);

            System.out.print("aspectRatioHeight:  ");
            System.out.println(aspectRatioHeight);          
  }
  });      

And aspectRatios are used inside run:

public void run() {
System.out.print("aspectRatioWidth:  ");
System.out.println(aspectRatioWidth);

System.out.print("aspectRatioHeight:  ");
System.out.println(aspectRatioHeight);

while(true){
    BufferedImage buff = robot.createScreenCapture(new Rectangle(bufx,bufy,bufwidth,bufheight)); // x-coord, y-coord, width, height

    BufferedImage resizedbuff = resizeImage(buff, aspectRatioWidth, aspectRatioHeight);}

Solution

  • You're doing int division which always returns an int, here 0 since your screen dimensions will likely be greater than your image dimensions:

    aspectRatioWidth = bufwidth/scrwidth;
    aspectRatioHeight = bufheight/scrheight;
    

    Solution: convert the numbers to double and then do double division.

    aspectRatioWidth = (double) bufwidth/scrwidth;
    aspectRatioHeight = (double) bufheight/scrheight;
    

    Edit

    Not sure what you're ultimately trying to do -- post the image of the computer screen in your GUI? If so, perhaps something like...

    import java.awt.AWTException;
    import java.awt.Dimension;
    import java.awt.Graphics;
    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.geom.AffineTransform;
    import java.awt.image.AffineTransformOp;
    import java.awt.image.BufferedImage;
    import java.util.List;
    
    import javax.swing.*;
    
    public class ChangeVars extends JPanel {
       private static final int PREF_W = 400;
       private static final int PREF_H = PREF_W;
       private static final int DELAY = 20;
       public BufferedImage displayImage;
       private MyWorker myWorker;
    
       public ChangeVars() {
          try {
             myWorker = new MyWorker(DELAY);
             myWorker.execute();
          } catch (AWTException e) {
             e.printStackTrace();
          }
       }
    
       @Override
       // to initialize the panel to something
       public Dimension getPreferredSize() {
          if (isPreferredSizeSet()) {
             return super.getPreferredSize();
          }
          return new Dimension(PREF_W, PREF_H);
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          if (displayImage != null) {
             g.drawImage(displayImage, 0, 0, null);
          }
       }
    
       public void stopWorker() {
          if (myWorker != null && !myWorker.isDone()) {
             myWorker.setRunning(false);
             myWorker.cancel(true);
          }
       }
    
       private class MyWorker extends SwingWorker<Void, BufferedImage> {
    
          private volatile boolean running = true;
          private Robot robot;
          private int delay;
    
          public MyWorker(int delay) throws AWTException {
             this.delay = delay;
             robot = new Robot();
          }
    
          @Override
          protected Void doInBackground() throws Exception {
             while (running) {
                Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
                Rectangle screenRect = new Rectangle(0, 0, d.width, d.height);
                BufferedImage img = robot.createScreenCapture(screenRect);
                publish(img);
                Thread.sleep(delay);
             }
             return null;
          }
    
          @Override
          protected void process(List<BufferedImage> chunks) {
             for (BufferedImage image : chunks) {
                Dimension sz = getSize();
                double scaleX = (double) sz.width / image.getWidth();
                double scaleY = (double) sz.height / image.getHeight();
                AffineTransform transform = AffineTransform.getScaleInstance(
                      scaleX, scaleY);
                AffineTransformOp transformOp = new AffineTransformOp(transform,
                      AffineTransformOp.TYPE_BILINEAR);
                displayImage = new BufferedImage(sz.width, sz.height,
                      BufferedImage.TYPE_INT_ARGB);
                displayImage = transformOp.filter(image, displayImage);
                repaint();
             }
          }
    
          public void setRunning(boolean running) {
             this.running = running;
          }
    
          public boolean getRunning() {
             return running;
          }
    
       }
    
       private static void createAndShowGui() {
          final ChangeVars changeVars = new ChangeVars();
    
          JFrame frame = new JFrame("ChangeVars");
          frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
          frame.addWindowListener(new WindowAdapter() {
    
             @Override
             public void windowClosing(WindowEvent e) {
                if (changeVars != null) {
                   changeVars.stopWorker();
                }
                System.exit(0);
             }
    
          });
          frame.getContentPane().add(changeVars);
          frame.pack();
          frame.setLocationByPlatform(true);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }
    

    Although simpler would be to just let paintComponent do the scaling:

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (displayImage != null) {
         int width = getWidth();
         int height = getHeight();
         g.drawImage(displayImage, 0, 0, width, height, null);
      }
    }
    
    // ....
    
      @Override
      protected void process(List<BufferedImage> chunks) {
         for (BufferedImage image : chunks) {
            displayImage = image;
            repaint();
         }
      }