/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.visualization.screenshot;
import org.gephi.visualization.opengl.*;
import com.sun.opengl.util.FileUtil;
import com.sun.opengl.util.ImageUtil;
import com.sun.opengl.util.TileRenderer;
import java.awt.Cursor;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLPbuffer;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.gephi.ui.utils.DialogFileFilter;
import org.gephi.visualization.VizArchitecture;
import org.gephi.visualization.VizController;
import org.gephi.visualization.apiimpl.VizConfig;
import org.gephi.visualization.opengl.text.TextManager;
import org.gephi.visualization.swing.GLAbstractListener;
import org.gephi.visualization.swing.GraphDrawableImpl;
import org.netbeans.validation.api.ui.ValidationPanel;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
import org.openide.windows.WindowManager;
/**
*
* @author Mathieu Bastian
*/
public class ScreenshotMaker implements VizArchitecture {
//Const
private final String LAST_PATH = "ScreenshotMaker_Last_Path";
private final String LAST_PATH_DEFAULT = "ScreenshotMaker_Last_Path_Default";
private final String ANTIALIASING_DEFAULT = "ScreenshotMaker_Antialiasing_Default";
private final String WIDTH_DEFAULT = "ScreenshotMaker_Width_Default";
private final String HEIGHT_DEFAULT = "ScreenshotMaker_Height_Default";
private final String TRANSPARENT_BACKGROUND_DEFAULT = "ScreenshotMaker_TransparentBackground_Default";
private final String AUTOSAVE_DEFAULT = "ScreenshotMaker_Autosave_Default";
private final String SHOW_MESSAGE = "ScreenshotMaker_Show_Message";
//Architecture
private GraphDrawableImpl drawable;
private AbstractEngine engine;
private TextManager textManager;
private VizConfig vizConfig;
//Settings
private int antiAliasing = 2;
private int width = 1024;
private int height = 768;
private boolean transparentBackground = false;
private boolean finishedMessage = true;
private boolean autoSave = false;
private String defaultDirectory;
//Running
private File file;
//State
private boolean takeTicket = false;
public ScreenshotMaker() {
//Preferences
String lastPathDefault = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH_DEFAULT, null);
defaultDirectory = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH, lastPathDefault);
antiAliasing = NbPreferences.forModule(ScreenshotMaker.class).getInt(ANTIALIASING_DEFAULT, antiAliasing);
width = NbPreferences.forModule(ScreenshotMaker.class).getInt(WIDTH_DEFAULT, width);
height = NbPreferences.forModule(ScreenshotMaker.class).getInt(HEIGHT_DEFAULT, height);
transparentBackground = NbPreferences.forModule(ScreenshotMaker.class).getBoolean(TRANSPARENT_BACKGROUND_DEFAULT, transparentBackground);
autoSave = NbPreferences.forModule(ScreenshotMaker.class).getBoolean(AUTOSAVE_DEFAULT, autoSave);
finishedMessage = NbPreferences.forModule(ScreenshotMaker.class).getBoolean(SHOW_MESSAGE, finishedMessage);
}
public void initArchitecture() {
drawable = VizController.getInstance().getDrawable();
engine = VizController.getInstance().getEngine();
textManager = VizController.getInstance().getTextManager();
vizConfig = VizController.getInstance().getVizConfig();
}
public void takeScreenshot() {
takeTicket = true;
}
private void take(File file) throws Exception {
//System.out.println("Take Screenshot to " + file.getName());
// Fix the image size for now
int tileWidth = width / 16;
int tileHeight = height / 12;
int imageWidth = width;
int imageHeight = height;
//Caps
GLCapabilities caps = new GLCapabilities();
caps.setAlphaBits(8);
caps.setDoubleBuffered(false);
caps.setHardwareAccelerated(true);
caps.setSampleBuffers(true);
caps.setNumSamples(antiAliasing);
//Buffer
GLPbuffer pbuffer = GLDrawableFactory.getFactory().createGLPbuffer(caps, null, tileWidth, tileHeight, null);
BufferedImage image = null;
if (transparentBackground) {
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_4BYTE_ABGR);
} else {
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR);
}
ByteBuffer imageBuffer = ByteBuffer.wrap(((DataBufferByte) image.getRaster().getDataBuffer()).getData());
//Tile rendering
TileRenderer tileRenderer = new TileRenderer();
tileRenderer.setTileSize(tileWidth, tileHeight, 0);
tileRenderer.setImageSize(imageWidth, imageHeight);
if (transparentBackground) {
tileRenderer.setImageBuffer(GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, imageBuffer);
} else {
tileRenderer.setImageBuffer(GL.GL_BGR, GL.GL_UNSIGNED_BYTE, imageBuffer);
}
tileRenderer.trPerspective(drawable.viewField, (float) imageWidth / (float) imageHeight, drawable.nearDistance, drawable.farDistance);
//Get gl
//GLContext oldContext = GLContext.getCurrent();
GLContext context = pbuffer.getContext();
if (context.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) {
throw new RuntimeException("Error making pbuffer's context current");
}
GL gl = pbuffer.getGL();
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
//Init
drawable.initConfig(gl);
vizConfig.setDisableLOD(true);
engine.initScreenshot(gl, GLAbstractListener.glu);
//Textrender - swap to 3D
textManager.setRenderer3d(true);
//Render in buffer
do {
tileRenderer.beginTile(gl);
drawable.renderScreenshot(pbuffer);
} while (tileRenderer.endTile(gl));
//Clean
context.release();
pbuffer.destroy();
//Textrender - back to 2D
textManager.setRenderer3d(false);
vizConfig.setDisableLOD(false);
//Write image
ImageUtil.flipImageVertically(image);
writeImage(image);
/*Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("png");
if (iter.hasNext()) {
ImageWriter writer = iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
//iwp.setCompressionType("DEFAULT");
//iwp.setCompressionMode(javax.imageio.ImageWriteParam.MODE_EXPLICIT);
//iwp.setCompressionQuality((int)(9*pngCompresssion));
FileImageOutputStream output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage img = new IIOImage(image, null, null);
writer.write(null, img, iwp);
writer.dispose();
}*/
//oldContext.makeCurrent();
}
private void writeImage(BufferedImage image) throws Exception {
if (!autoSave) {
//Get last directory
String lastPathDefault = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH_DEFAULT, null);
String lastPath = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH, lastPathDefault);
final JFileChooser chooser = new JFileChooser(lastPath);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setDialogTitle(NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.filechooser.title"));
DialogFileFilter dialogFileFilter = new DialogFileFilter(NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.filechooser.pngDescription"));
dialogFileFilter.addExtension("png");
chooser.addChoosableFileFilter(dialogFileFilter);
File selectedFile = new File(chooser.getCurrentDirectory(), getDefaultFileName() + ".png");
chooser.setSelectedFile(selectedFile);
int returnFile = chooser.showSaveDialog(null);
if (returnFile != JFileChooser.APPROVE_OPTION) {
return;
}
file = chooser.getSelectedFile();
if (!file.getPath().endsWith(".png")) {
file = new File(file.getPath() + ".png");
}
//Save last path
defaultDirectory = file.getParentFile().getAbsolutePath();
NbPreferences.forModule(ScreenshotMaker.class).put(LAST_PATH, defaultDirectory);
} else {
file = new File(defaultDirectory, getDefaultFileName() + ".png");
}
String format = "png";
if (file != null) {
format = FileUtil.getFileSuffix(file);
}
if (!ImageIO.write(image, format, file)) {
throw new IOException("Unsupported file format");
}
}
public void openglSignal(GLAutoDrawable drawable) {
if (takeTicket) {
takeTicket = false;
try {
beforeTaking();
take(file);
drawable.getContext().makeCurrent();
afterTaking();
file = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void beforeTaking() {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
private void afterTaking() {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getDefaultCursor());
if (finishedMessage && file != null) {
final String msg = NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.finishedMessage.message", file.getName());
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), msg, NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.finishedMessage.title"), JOptionPane.INFORMATION_MESSAGE);
}
});
}
}
private static final String DATE_FORMAT_NOW = "HHmmss";
private String getDefaultFileName() {
Calendar cal = Calendar.getInstance();
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_NOW);
String datetime = dateFormat.format(cal.getTime());
return "screenshot_" + datetime;
}
public void configure() {
ScreenshotSettingsPanel panel = new ScreenshotSettingsPanel();
panel.setup(this);
ValidationPanel validationPanel = ScreenshotSettingsPanel.createValidationPanel(panel);
if (validationPanel.showOkCancelDialog(NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.configure.title"))) {
panel.unsetup(this);
return;
}
// DialogDescriptor dd = new DialogDescriptor(validationPanel, NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.configure.title"));
// Object result = DialogDisplayer.getDefault().notify(dd);
// if (result == NotifyDescriptor.OK_OPTION) {
// panel.unsetup(this);
// }
}
public int getAntiAliasing() {
return antiAliasing;
}
public void setAntiAliasing(int antiAliasing) {
this.antiAliasing = antiAliasing;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public boolean isAutoSave() {
return autoSave;
}
public void setAutoSave(boolean autoSave) {
this.autoSave = autoSave;
}
public boolean isTransparentBackground() {
return transparentBackground;
}
public void setTransparentBackground(boolean transparentBackground) {
this.transparentBackground = transparentBackground;
}
public String getDefaultDirectory() {
return defaultDirectory;
}
public void setDefaultDirectory(File directory) {
if (directory != null && directory.exists()) {
defaultDirectory = directory.getAbsolutePath();
NbPreferences.forModule(ScreenshotMaker.class).put(LAST_PATH, defaultDirectory);
}
}
}