Take screenshot generates twisted image

Hi,



When I call the take screen shot function, the generated image appears twisted, it looks like the width of the image is wrong and pixels are shifted to the next line. Not sure if it's some bug or if it's something I'm doing wrong… if someone could help me I'd appreciate.



Couldn't attach an image, but it looks a bit like it was processed with the (GIMP) shear tool.



Here's the testcase I'm using.



package screenshot;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.concurrent.Callable;

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

import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.shape.Box;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.SimpleCanvasImpl;
import com.jme.system.lwjgl.LWJGLSystemProvider;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;
import com.jmex.awt.lwjgl.LWJGLCanvas;

public class ScreenShot {

   private static Implementor impl;

   public static void main(String[] args) {
      JFrame f = new JFrame("test");
      f.setLayout(new BorderLayout());
      JPanel panel = new JPanel();
      JButton shotButton = new JButton("take shot");
      shotButton.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            Callable<Void> takeShot = new Callable<Void>() {
               @Override
               public Void call() throws Exception {
                  impl.getRenderer().takeScreenShot("/tmp/shot");
                  return null;
               }
            };
            GameTaskQueueManager.getManager().getQueue(GameTaskQueue.UPDATE).enqueue(takeShot);
         }
      });
      panel.add(shotButton);
      f.getContentPane().add(panel, BorderLayout.LINE_START);
      f.getContentPane().add(createCanvas(), BorderLayout.CENTER);
      f.setSize(new Dimension(800, 600));
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.setVisible(true);
   }

   private static LWJGLCanvas createCanvas() {
      DisplaySystem display = DisplaySystem.getDisplaySystem(LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER);
      display.registerCanvasConstructor("AWT", LWJGLAWTCanvasConstructor.class);
      LWJGLCanvas canvas = (LWJGLCanvas) display.createCanvas(100, 100);
      canvas.setUpdateInput(false);
      canvas.setTargetRate(60);
      impl = new Implementor(100, 100);
      canvas.setImplementor(impl);
      impl.setCanvas(canvas);

      canvas.addComponentListener(new ComponentAdapter() {
         public void componentResized(ComponentEvent ce) {
            impl.resizeCanvas(ce.getComponent().getWidth(), ce.getComponent().getHeight());
         }
      });

      return canvas;
   }
}

class Implementor extends SimpleCanvasImpl {

   private LWJGLCanvas canvas;

   protected Implementor(int width, int height) {
      super(width, height);
   }

   @Override
   public void simpleSetup() {
      resizeCanvas(canvas.getWidth(), canvas.getHeight());
      setBackground(ColorRGBA.white);
      Box box = new Box("Box", new Vector3f(-5,-5,-5), new Vector3f(5,5,5));
      box.setDefaultColor(ColorRGBA.blue);
      rootNode.attachChild(box);
   }

   @Override
   public void resizeCanvas(int newWidth, int newHeight) {
      super.resizeCanvas(newWidth, newHeight);
      if (cam != null) {
         cam.setFrustumPerspective(45.0f, (float) newWidth/(float) newHeight, 1, 5000);
      }
   }

   public void setCanvas(LWJGLCanvas canvas) {
      this.canvas = canvas;
   }
}

I think its because the displaysystems width and height are not updated when resizing the canvas.

There are similar problems with applets, thats why we had created a com.jmex.awt.applet.AppletResizeListener.



It should propably be renamed to CanvasResizeListener.

Use this instead of your Listener, then it works.



import java.awt.Canvas;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.concurrent.Callable;

import com.jme.system.DisplaySystem;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;

/**
 * Listens for resize events from an Canvas and reinitializes the Renderer.
 */
public class CanvasResizeListener implements ComponentListener {
   private Canvas canvas;
   
   public CanvasResizeListener(Canvas Canvas) {
      this.canvas = Canvas;
   }
   
   public void componentHidden(ComponentEvent ce) {

   }

   public void componentMoved(ComponentEvent ce) {

   }

   /**
    * Reinitializes the renderer based on the Canvass new size.<br>
    * Sets the new width and height in the displaysystem.
    */
   public void componentResized(final ComponentEvent ce) {
      Callable<?> exe = new Callable<Object>() {
         int w = canvas.getWidth();
         int h = canvas.getHeight();
         
          public Object call() {
             DisplaySystem display = DisplaySystem.getDisplaySystem();
             display.getRenderer().reinit(w, h);
             display.getRenderer().getCamera().setFrustumPerspective(45.0f,
                   (float) canvas.getWidth() / (float)canvas.getHeight(), 1, 1000);
             display.setWidth(w);
             display.setHeight(h);
             return null;
          }
      };
      GameTaskQueueManager.getManager()
         .getQueue(GameTaskQueue.RENDER).enqueue(exe);
   }

   public void componentShown(ComponentEvent ce) {

   }

}

Thanks for the reply. But so far no luck here… the new listener didn't change the behaviour.



The image in the screen is appearing correctly, so I think the resize was/is working fine. The png image is also generated with the correct dimension. One thing I noticed is that when I resize the canvas, sometimes the shot is generated correctly and sometimes it also distorts the colors, I get the twisted square drawn as a sequence of red, green and blue lines.

hmm strange, it worked for me, did you remove your other listener ?

Yep, removed the other listener and I was printing the dimension of both renderer and display to make sure they were correct. I also built jme from svn and tried with lwjgl 2.0, but no luck.



What is working for me is when I change the takeScreenShot code to call "grabScreenContents(buff, Image.Format.RGB8, 0, 0, width, height)" using Image.Format.RGBA8 instead of Image.Format.RGB8. Then it works every time.



I tried this on winXP/intel, Fedora11/intel and Fedora11/nvidia, the video cards are some years old thought.

ah sorry i don't know whats going on, it seems the Canvaslistener really doesn't help, not sure why i got a correct result yesterday.