* OGLESShaderRenderer.readFrameBuffer() can now read from the main framebuffer in RGBA format.

* Add JmeSystem.writeImageFile() for a platform independent image writing method. Desktop and Android implementations are available.
 * ScreenshotAppState no longer depends on AWT or Desktop Java, instead it uses JmeSystem.writeImageFile() which will run on Android as well.


git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9571 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Sha..rd 13 years ago
parent a3255e4f45
commit c7186886bc
  1. 15
      engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
  2. 26
      engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
  3. 42
      engine/src/android/com/jme3/util/AndroidScreenshots.java
  4. 8
      engine/src/core/com/jme3/system/JmeSystem.java
  5. 5
      engine/src/core/com/jme3/system/JmeSystemDelegate.java
  6. 30
      engine/src/desktop/com/jme3/app/state/ScreenshotAppState.java
  7. 12
      engine/src/desktop/com/jme3/system/JmeDesktopSystem.java
  8. 6
      engine/src/desktop/com/jme3/util/Screenshots.java

@ -1271,8 +1271,21 @@ public class OGLESShaderRenderer implements Renderer {
} }
*/ */
/**
* Reads the Color Buffer from OpenGL and stores into the ByteBuffer.
* Since jME for Android does not support Frame Buffers yet, make sure the FrameBuffer
* passed in is NULL (default) or an exception will be thrown.
* Also, make sure to call setViewPort with the appropriate viewport size before
* calling readFrameBuffer.
* @param fb FrameBuffer (must be NULL)
* @param byteBuf ByteBuffer to store the Color Buffer from OpenGL
*/
public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
logger.warning("readFrameBuffer is not supported."); if (fb != null) {
throw new IllegalArgumentException("FrameBuffer is not supported yet.");
}
GLES20.glReadPixels(vpX, vpY, vpW, vpH, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuf);
} }
/* /*
public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf){ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf){

@ -2,19 +2,21 @@ package com.jme3.system.android;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.graphics.Bitmap;
import android.os.Environment; import android.os.Environment;
import com.jme3.asset.AndroidAssetManager; import com.jme3.asset.AndroidAssetManager;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer; import com.jme3.audio.AudioRenderer;
import com.jme3.audio.android.AndroidAudioRenderer; import com.jme3.audio.android.AndroidAudioRenderer;
import com.jme3.system.AppSettings; import com.jme3.system.*;
import com.jme3.system.JmeContext;
import com.jme3.system.JmeContext.Type; import com.jme3.system.JmeContext.Type;
import com.jme3.system.JmeSystemDelegate; import com.jme3.util.AndroidScreenshots;
import com.jme3.system.Platform;
import com.jme3.util.JmeFormatter; import com.jme3.util.JmeFormatter;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -23,6 +25,22 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
private static Activity activity; private static Activity activity;
@Override
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
Bitmap bitmapImage = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
AndroidScreenshots.convertScreenShot(imageData, bitmapImage);
Bitmap.CompressFormat compressFormat;
if (format.equals("png")) {
compressFormat = Bitmap.CompressFormat.PNG;
} else if (format.equals("jpg")) {
compressFormat = Bitmap.CompressFormat.JPEG;
} else {
throw new UnsupportedOperationException("Only 'png' and 'jpg' formats are supported on Android");
}
bitmapImage.compress(compressFormat, 95, outStream);
bitmapImage.recycle();
}
static { static {
try { try {
System.loadLibrary("bulletjme"); System.loadLibrary("bulletjme");

@ -0,0 +1,42 @@
package com.jme3.util;
import android.graphics.Bitmap;
import java.nio.ByteBuffer;
import java.util.logging.Logger;
public final class AndroidScreenshots {
private static final Logger logger = Logger.getLogger(AndroidScreenshots.class.getName());
/**
* Convert OpenGL GLES20.GL_RGBA to Bitmap.Config.ARGB_8888 and store result
* in a Bitmap
*
* @param buf ByteBuffer that has the pixel color data from OpenGL
* @param bitmapImage Bitmap to be used after converting the data
*/
public static void convertScreenShot(ByteBuffer buf, Bitmap bitmapImage) {
int width = bitmapImage.getWidth();
int height = bitmapImage.getHeight();
int size = width * height;
// Grab data from ByteBuffer as Int Array to manipulate data and send to image
int[] data = new int[size];
buf.asIntBuffer().get(data);
// convert from GLES20.GL_RGBA to Bitmap.Config.ARGB_8888
// ** need to swap RED and BLUE **
for (int idx = 0; idx < data.length; idx++) {
int initial = data[idx];
int pb = (initial >> 16) & 0xff;
int pr = (initial << 16) & 0x00ff0000;
int pix1 = (initial & 0xff00ff00) | pr | pb;
data[idx] = pix1;
}
// OpenGL and Bitmap have opposite starting points for Y axis (top vs bottom)
// Need to write the data in the image from the bottom to the top
// Use size-width to indicate start with last row and increment by -width for each row
bitmapImage.setPixels(data, size - width, -width, 0, 0, width, height);
}
}

@ -35,8 +35,11 @@ import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer; import com.jme3.audio.AudioRenderer;
import com.jme3.input.SoftTextDialogInput; import com.jme3.input.SoftTextDialogInput;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -93,6 +96,11 @@ public class JmeSystem {
return systemDelegate.getSoftTextDialogInput(); return systemDelegate.getSoftTextDialogInput();
} }
public static void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
checkDelegate();
systemDelegate.writeImageFile(outStream, format, imageData, width, height);
}
public static AssetManager newAssetManager(URL configFile) { public static AssetManager newAssetManager(URL configFile) {
checkDelegate(); checkDelegate();
return systemDelegate.newAssetManager(configFile); return systemDelegate.newAssetManager(configFile);

@ -35,8 +35,11 @@ import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer; import com.jme3.audio.AudioRenderer;
import com.jme3.input.SoftTextDialogInput; import com.jme3.input.SoftTextDialogInput;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -96,6 +99,8 @@ public abstract class JmeSystemDelegate {
return softTextDialogInput; return softTextDialogInput;
} }
public abstract void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException;
public abstract AssetManager newAssetManager(URL configFile); public abstract AssetManager newAssetManager(URL configFile);
public abstract AssetManager newAssetManager(); public abstract AssetManager newAssetManager();

@ -10,17 +10,17 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue;
import com.jme3.system.JmeSystem;
import com.jme3.texture.FrameBuffer; import com.jme3.texture.FrameBuffer;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.Screenshots;
import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.imageio.ImageIO;
public class ScreenshotAppState extends AbstractAppState implements ActionListener, SceneProcessor { public class ScreenshotAppState extends AbstractAppState implements ActionListener, SceneProcessor {
@ -30,7 +30,7 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
private ByteBuffer outBuf; private ByteBuffer outBuf;
private String appName; private String appName;
private int shotIndex = 0; private int shotIndex = 0;
private BufferedImage awtImage; private int width, height;
@Override @Override
public void initialize(AppStateManager stateManager, Application app) { public void initialize(AppStateManager stateManager, Application app) {
@ -66,8 +66,9 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
} }
public void reshape(ViewPort vp, int w, int h) { public void reshape(ViewPort vp, int w, int h) {
outBuf = BufferUtils.createByteBuffer(w*h*4); outBuf = BufferUtils.createByteBuffer(w * h * 4);
awtImage = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR); width = w;
height = h;
} }
public void preFrame(float tpf) { public void preFrame(float tpf) {
@ -82,13 +83,22 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
shotIndex++; shotIndex++;
renderer.readFrameBuffer(out, outBuf); renderer.readFrameBuffer(out, outBuf);
Screenshots.convertScreenShot(outBuf, awtImage); File file = new File(appName + shotIndex + ".png").getAbsoluteFile();
OutputStream outStream = null;
try {
outStream = new FileOutputStream(file);
JmeSystem.writeImageFile(outStream, "png", outBuf, width, height);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error while saving screenshot", ex);
} finally {
if (outStream != null){
try { try {
ImageIO.write(awtImage, "png", new File(appName + shotIndex + ".png")); outStream.close();
} catch (IOException ex){ } catch (IOException ex) {
logger.log(Level.SEVERE, "Error while saving screenshot", ex); logger.log(Level.SEVERE, "Error while saving screenshot", ex);
} }
} }
} }
}
}
} }

@ -38,12 +38,17 @@ import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.DesktopAssetManager; import com.jme3.asset.DesktopAssetManager;
import com.jme3.audio.AudioRenderer; import com.jme3.audio.AudioRenderer;
import com.jme3.system.JmeContext.Type; import com.jme3.system.JmeContext.Type;
import com.jme3.util.Screenshots;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level; import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@ -58,6 +63,13 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
return new DesktopAssetManager(configFile); return new DesktopAssetManager(configFile);
} }
@Override
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
BufferedImage awtImage = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
Screenshots.convertScreenShot(imageData, awtImage);
ImageIO.write(awtImage, format, outStream);
}
@Override @Override
public AssetManager newAssetManager() { public AssetManager newAssetManager() {
return new DesktopAssetManager(null); return new DesktopAssetManager(null);

@ -33,6 +33,12 @@ public final class Screenshots {
// } // }
} }
/**
* Flips the image along the Y axis and converts BGRA to ABGR
*
* @param bgraBuf
* @param out
*/
public static void convertScreenShot(ByteBuffer bgraBuf, BufferedImage out){ public static void convertScreenShot(ByteBuffer bgraBuf, BufferedImage out){
WritableRaster wr = out.getRaster(); WritableRaster wr = out.getRaster();
DataBufferByte db = (DataBufferByte) wr.getDataBuffer(); DataBufferByte db = (DataBufferByte) wr.getDataBuffer();

Loading…
Cancel
Save