2015-05-30 29 views
6

Próbowałem zmiany rozmiaru obrazu za pomocą buforowanego AffineTransform jak Scalr.resizeDynamiczne zmiany rozmiaru BufferedImage w java

Oto moje kody dla obu z nich.

użyciu 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); 

użyciu 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); 

zmienne zostały zadeklarowane na początku wewnątrz klasy:

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; 

otrzymuję wartości dla wszystkich zmiennych dynamicznie w innej metodzie:

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

Jednakże gdy ten kod pojawia się błąd w obie funkcje AffineTransform jak 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) 

Jak mogę to zrobić? Rozumiem, że tak się dzieje, ponieważ zmienię zmienną w inny sposób i uzyskuję do niej dostęp w innej. Ale tych dwóch metod nie można łączyć. Czy mogę w jakiś sposób to wykonać?

EDIT:

zmieniłem metodę zmiany rozmiaru Oto co zrobiłem teraz

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):

Dla jaśniejszego pojęcia, co się dzieje dokładnie:

To jest metoda, która ponownie Okazuje scrwidth i 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");}} 

I to jest ActionListener która zwraca bufwidth i 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);   
    } 
    });  

I aspectRatios są używane wewnątrz 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);} 
+0

Więc ... po dokonaniu pewnych zmian (masz dokonał zmienić, które poleciłem?) - co zrobiły twoje zmiany? Czy Twój kod działa? Czy nadal widzisz wyjątek? –

+0

yes Przekonwertowałem liczby na podwójne. To daje właściwą wartość, kiedy wykonać podział wewnątrz metody, która wraca bufwidth, bufheight i wszystkie zmienne Jednak wraca do zera kiedy wydrukować aspectRatioWidth i aspectRatioHeight wewnątrz run() Rozumiem, że to się dzieje, ponieważ po uruchomieniu funkcji run() wartości wszystkich zmiennych nie zostały jeszcze zwiększone. Jak mogę się upewnić, że funkcja run() występuje tylko po zwiększeniu wartości? – newbie2015

Odpowiedz

4

Robisz int podział, który zawsze zwraca wartość int, tutaj 0, ponieważ wymiary ekranu będą prawdopodobnie większe niż wymiary obrazu:

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

Rozwiązanie: zamień liczby na podwójne, a następnie wykonaj podwójny podział.

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

Edit

Nie wiesz, co masz w końcu próbuje zrobić - umieść obraz na ekranie komputera w GUI? Jeśli tak, być może coś takiego ...

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

Choć prościej byłoby po prostu pozwolić paintComponent zrobić skalowanie:

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

Używam innej metody do implementacji skalowania obrazu Działa dobrze, ale teraz zdałem sobie sprawę, że istnieje inny problem Dwie zmienne zmieniają się w innej funkcji, podczas gdy ja uzyskuję do nich dostęp w trybie run() Tak, gdy aspectRatioWidth i aspectRatioHeight zawsze zwracają zero, ponieważ bufwidth i bufheight zostały zainicjowane do zera Czy jest jakiś sposób, aby upewnić się, że inne funkcje są realizowane przed uruchomieniem() – newbie2015

+0

@ newbie2015: więc teraz zmieniasz wymagania na nas. W przyszłości należy tego unikać, ponieważ może to być dla nas frustrujące i może unieważnić istniejące odpowiedzi. Jeśli chodzi o Twój problem, jeśli masz teraz nowy problem, edytuj swoje pytanie i wyjaśnij jak najwięcej problemów. Nie jestem pewien, czy mogę w pełni zrozumieć, co jest nie tak, na podstawie blurbu zamieszczonego powyżej. Na przykład nie wiem, jak te zmienne są ze sobą powiązane. –

+0

@ newbie2015: rozważ utworzenie i opublikowanie [Minimalny, kompletny i weryfikowalny przykładowy program] (http://stackoverflow.com/help/mcve), w którym skondensujesz swój kod w najmniejszym bitem, który wciąż kompiluje i działa, nie ma na zewnątrz zależności (takie jak potrzeba połączenia z bazą danych lub obrazami), nie ma dodatkowego kodu, który nie jest istotny dla twojego problemu, ale nadal pokazuje twój problem. Jako problem uboczny, twój obecny kod wygląda na zbyt intensywne używanie pól statycznych, czego zasadniczo należy unikać. –