/*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*
* Sun gratefully acknowledges that this software was originally authored
* and developed by Kenneth Bradley Russell and Christopher John Kline.
*/
package demos.texture;
import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureCoords;
import com.jogamp.opengl.util.texture.TextureData;
import com.jogamp.opengl.util.texture.TextureIO;
import com.jogamp.opengl.util.texture.awt.AWTTextureIO;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GL;
import javax.media.opengl.GL2ES1;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
/** Demonstrates and tests updates of sub-rectangles of a Texture
generated from a BufferedImage. Draws a cursor in texel space
around the cursor using Java 2D's XOR mode and pushes those
updates to an underlying OpenGL texture using
Texture.updateSubImage(). */
public class TestSubImage {
private boolean haveForcedImageType;
private int forcedImageType;
private List imageTypeMenuItems = new ArrayList();
private GLU glu = new GLU();
private GLCanvas mainCanvas;
private Rectangle curRect;
private Rectangle lastRect;
private TextureData textureData;
private Texture texture;
private BufferedImage baseImage;
private BufferedImage convertedImage;
private int CURSOR_SIZE = 10;
class Listener implements GLEventListener {
public void init(GLAutoDrawable drawable) {}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
GL2 gl = drawable.getGL().getGL2();
gl.glMatrixMode(GL2ES1.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluOrtho2D(0, 1, 0, 1);
gl.glMatrixMode(GL2ES1.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void dispose(GLAutoDrawable drawable) {
}
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
if (convertedImage == null) {
// Get rid of any previously allocated texture
if (texture != null) {
texture.dispose();
texture = null;
}
// Convert the base image
// Note that for simplicity in handling of the XOR cursor, we
// always make a copy of the base image
int imageType = BufferedImage.TYPE_INT_RGB;
if (haveForcedImageType) {
imageType = forcedImageType;
}
convertedImage = new BufferedImage(baseImage.getWidth(),
baseImage.getHeight(),
imageType);
Graphics2D g = convertedImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(baseImage, 0, 0, null);
g.dispose();
// Create a TextureData and Texture from it
textureData = AWTTextureIO.newTextureData(gl.getGLProfile(), convertedImage, false);
texture = TextureIO.newTexture(textureData);
}
Graphics2D g = convertedImage.createGraphics();
g.setColor(Color.RED);
g.setXORMode(Color.WHITE);
if (lastRect != null) {
// Paint with XOR in this area
g.fillRect(lastRect.x, lastRect.y, lastRect.width, lastRect.height);
}
if (curRect != null) {
// Paint with XOR in this area
g.fillRect(curRect.x, curRect.y, curRect.width, curRect.height);
}
// Compute the union of these rectangles, if any
Rectangle union = null;
if (lastRect != null) {
union = new Rectangle(lastRect);
if (curRect != null) {
union.add(curRect);
}
} else if (curRect != null) {
union = curRect;
}
// Move these down
lastRect = curRect;
curRect = null;
// Update the affected area of the texture (if there is one)
// Note: this one API call is basically what this demo is illustrating
if (union != null) {
int yOrigin = union.y;
// Note: if the Y origin of the texture data didn't match Java
// 2D's Y origin, we would need to correct the Y coordinate
// passed down here. This code path is not taken in the
// current Texture implementation.
if (!texture.getMustFlipVertically()) {
yOrigin = texture.getHeight() - yOrigin;
}
texture.updateSubImage(textureData, 0,
union.x, yOrigin,
union.x, yOrigin,
union.width, union.height);
}
// Now draw one quad with the texture
texture.enable();
texture.bind();
gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE);
TextureCoords coords = texture.getImageTexCoords();
gl.glBegin(GL2.GL_QUADS);
gl.glTexCoord2f(coords.left(), coords.bottom());
gl.glVertex3f(0, 0, 0);
gl.glTexCoord2f(coords.right(), coords.bottom());
gl.glVertex3f(1, 0, 0);
gl.glTexCoord2f(coords.right(), coords.top());
gl.glVertex3f(1, 1, 0);
gl.glTexCoord2f(coords.left(), coords.top());
gl.glVertex3f(0, 1, 0);
gl.glEnd();
texture.disable();
}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
}
private JMenuItem newItem(final int imageType, String imageTypeName) {
final JCheckBoxMenuItem item = new JCheckBoxMenuItem(imageTypeName);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (item.isSelected()) {
// Disable all other checkbox menu items
for (Iterator iter = imageTypeMenuItems.iterator(); iter.hasNext(); ) {
JCheckBoxMenuItem cb = (JCheckBoxMenuItem) iter.next();
if (cb != item)
cb.setSelected(false);
}
haveForcedImageType = true;
forcedImageType = imageType;
} else {
// No longer have a forced image type
haveForcedImageType = false;
}
// Get rid of the previously created converted image, if any
if (convertedImage != null) {
convertedImage.flush();
convertedImage = null;
}
lastRect = null;
mainCanvas.repaint();
}
});
imageTypeMenuItems.add(item);
return item;
}
private void run() {
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
// Create top-level window and menus
JFrame frame = new JFrame("Texture Sub Image Test");
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
menu.setMnemonic(KeyEvent.VK_F);
JMenuItem item = new JMenuItem("Quit");
item.setMnemonic(KeyEvent.VK_Q);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_MASK));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
menu.add(item);
menuBar.add(menu);
menu = new JMenu("Tools");
menu.setMnemonic(KeyEvent.VK_T);
JMenu submenu = new JMenu("Force BufferedImage Type");
submenu.setMnemonic(KeyEvent.VK_F);
submenu.add(newItem(BufferedImage.TYPE_3BYTE_BGR, "TYPE_3BYTE_BGR"));
submenu.add(newItem(BufferedImage.TYPE_4BYTE_ABGR, "TYPE_4BYTE_ABGR"));
submenu.add(newItem(BufferedImage.TYPE_4BYTE_ABGR_PRE, "TYPE_4BYTE_ABGR_PRE"));
submenu.add(newItem(BufferedImage.TYPE_BYTE_BINARY, "TYPE_BYTE_BINARY"));
submenu.add(newItem(BufferedImage.TYPE_BYTE_GRAY, "TYPE_BYTE_GRAY"));
submenu.add(newItem(BufferedImage.TYPE_BYTE_INDEXED, "TYPE_BYTE_INDEXED"));
submenu.add(newItem(BufferedImage.TYPE_INT_ARGB, "TYPE_INT_ARGB"));
submenu.add(newItem(BufferedImage.TYPE_INT_ARGB_PRE, "TYPE_INT_ARGB_PRE"));
submenu.add(newItem(BufferedImage.TYPE_INT_BGR, "TYPE_INT_BGR"));
submenu.add(newItem(BufferedImage.TYPE_INT_RGB, "TYPE_INT_RGB"));
submenu.add(newItem(BufferedImage.TYPE_USHORT_555_RGB, "TYPE_USHORT_555_RGB"));
submenu.add(newItem(BufferedImage.TYPE_USHORT_565_RGB, "TYPE_USHORT_565_RGB"));
submenu.add(newItem(BufferedImage.TYPE_USHORT_GRAY, "TYPE_USHORT_GRAY"));
menu.add(submenu);
menuBar.add(menu);
frame.setJMenuBar(menuBar);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Set up the base image we'll use to draw with
baseImage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
Graphics2D g = baseImage.createGraphics();
g.setPaint(new GradientPaint(0, 0, Color.CYAN,
baseImage.getWidth(), baseImage.getHeight(), Color.BLUE));
g.fillRect(0, 0, baseImage.getWidth(), baseImage.getHeight());
g.dispose();
// Now set up the main GLCanvas
mainCanvas = new GLCanvas();
mainCanvas.addGLEventListener(new Listener());
mainCanvas.addMouseListener(new MouseAdapter() {
public void mouseExited(MouseEvent e) {
curRect = null;
mainCanvas.repaint();
}
});
mainCanvas.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
// Scale x and y into coordinate system of texture
int x = (int) (e.getX() * (float) baseImage.getWidth() / (float) e.getComponent().getWidth());
int y = (int) (e.getY() * (float) baseImage.getHeight() / (float) e.getComponent().getHeight());
// Paint cursor on texture around this point
int minx = Math.max(0, x - CURSOR_SIZE);
int maxx = Math.min(baseImage.getWidth(), x + CURSOR_SIZE);
int miny = Math.max(0, y - CURSOR_SIZE);
int maxy = Math.min(baseImage.getHeight(), y + CURSOR_SIZE);
curRect = new Rectangle(minx, miny, maxx - minx, maxy - miny);
mainCanvas.repaint();
}
});
frame.getContentPane().add(mainCanvas);
frame.setSize(512, 512);
frame.setVisible(true);
}
public static void main(String[] args) {
// set argument 'NotFirstUIActionOnProcess' in the JNLP's application-desc tag for example
//
// NotFirstUIActionOnProcess
//
boolean firstUIActionOnProcess = 0==args.length || !args[0].equals("NotFirstUIActionOnProcess") ;
GLProfile.initSingleton(firstUIActionOnProcess);
new TestSubImage().run();
}
}