Proper way to draw images in GLJpanel - java

I m using the GLJpanel component in my Java swing application, I want to draw images that are Frames from FFmpegFrameGrabber in my GLJPanel, to do so my idea was to use the component graphics as mentioned below.
import org.bytedeco.javacv.Frame;
import com.jogamp.opengl.awt.GLJPanel;
public void showImage(GLJPanel panel, Frame frame) {
Graphics2D graphics = (Graphics2D) panel.getGraphics();
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bfimage = converter.convert(frame);
graphics.drawImage(bfimage, null, 0,0);
}
Is this the proper way to draw images in GL enabled Components or there is another way, I have doubt that I m wrong but I can't prove it
My GLJPanel was created as below
final GLProfile profile = GLProfile.get(GLProfile.GL2);
GLCapabilities capabilities = new GLCapabilities(profile);
GLJPanel panel = new GLJPanel(capabilities);

All I had to do is to create a GLEventListener, this interface have 4 methods display, displayChanged, init and dispose then the common parameter GLAutoDrawable which is responsible for drawing
in case of drawing images BufferedImage I believe that the first step is to convert any image input to BufferedImage in my case I enclosed this behaviour in a utility class and fed a queue with my converted images.
to draw you have to use a Texture this one can be created using a TextureData which will use the buffered image as follow
TextureData textureData = AWTTextureIO.newTextureData(gl.getGLProfile(), baseImage, false);
Texture texture = TextureIO.newTexture(textureData);
So finally my display should be like
public synchronized void display(GLAutoDrawable drawable) {
BufferedImage baseImage = frames.poll();
if(baseImage == null)return;
final GL2 gl = drawable.getGL().getGL2();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
// Create a TextureData and Texture from it
TextureData textureData = AWTTextureIO.newTextureData(gl.getGLProfile(), baseImage, false);
Texture texture = TextureIO.newTexture(textureData);
// Now draw one quad with the texture
texture.enable(gl);
texture.bind(gl);
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(gl);
texture.destroy(gl);
//baseImage = null;
}

Related

Java: Apply different textures to cube faces and render image to png with JOGL

I want to render a cube with three faces visible, each of those faces should have a different texture applied to it, which should be easily interchangable. I managed to get this basic code running, that only works with colors.
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLJPanel;
import javax.swing.*;
import java.awt.*;
import static com.jogamp.opengl.GL.GL_MULTISAMPLE;
public class CubeRenderer extends GLJPanel implements GLEventListener {
public static void main(String[] args) {
JFrame window = new JFrame("JOGL Scene");
GLCapabilities caps = new GLCapabilities(null);
CubeRenderer panel = new CubeRenderer(caps);
window.setContentPane(panel);
window.pack();
window.setLocation(50,50);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
panel.requestFocusInWindow();
}
private final float rotateX;
private final float rotateY;
private final float rotateZ; // rotation amounts about axes
private int texture;
// Correct orientation -45.0, 150.0, 90.0
public CubeRenderer(GLCapabilities capabilities) {
super(capabilities);
setPreferredSize( new Dimension(500,500) );
addGLEventListener(this);
rotateX = -45.0f;
rotateY = 150.0f;
rotateZ = 90.0f;
}
private void square(GL2 gl, float r, float g, float b) {
gl.glColor3f(r,g,b); // The color for the square.
gl.glTranslatef(0,0,0.5f); // Move square 0.5 units forward.
gl.glNormal3f(0,0,1); // Normal vector to square (this is actually the default).
gl.glBegin(GL2.GL_TRIANGLE_FAN);
gl.glVertex2f(-0.5f,-0.5f); // Draw the square (before the
gl.glVertex2f(0.5f,-0.5f); // the translation is applied)
gl.glVertex2f(0.5f,0.5f); // on the xy-plane, with its
gl.glVertex2f(-0.5f,0.5f); // at (0,0,0).
gl.glEnd();
}
private void cube(GL2 gl) {
gl.glPushMatrix();
gl.glRotatef(180,0,1,0); // rotate square to back face
square(gl,0,1,1); // back face is cyan
gl.glPopMatrix();
gl.glPushMatrix();
gl.glRotatef(-90,0,1,0); // rotate square to left face
square(gl,0,1,0); // left face is green
gl.glPopMatrix();
gl.glPushMatrix();
gl.glRotatef(-90,1,0,0); // rotate square to top face
square(gl,0,0,1); // top face is blue
gl.glPopMatrix();
}
public void display(GLAutoDrawable drawable) {
// called when the panel needs to be drawn
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(0,0,0,0);
gl.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
gl.glMatrixMode(GL2.GL_PROJECTION); // Set up the projection.
gl.glLoadIdentity();
gl.glOrtho(-1,1,-1,1,-2,2);
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity(); // Set up modelview transform.
gl.glRotatef(rotateZ,0,0,1);
gl.glRotatef(rotateY,0,1,0);
gl.glRotatef(rotateX,1,0,0);
cube(gl);
}
public void init(GLAutoDrawable drawable) {
// called when the panel is created
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(0.8F, 0.8F, 0.8F, 1.0F);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glEnable(GL2.GL_LIGHTING);
gl.glEnable(GL2.GL_LIGHT0);
gl.glEnable(GL2.GL_COLOR_MATERIAL);
gl.glEnable(GL_MULTISAMPLE);
}
public void dispose(GLAutoDrawable drawable) {
// called when the panel is being disposed
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
// called when user resizes the window
}
}
I however could not figure out how to apply texture instead of colors and how to then render the whole cube into a png, as I could not get any of these tutorials to run, as most of them were quite old.
Either merge the 3 images into a single image, create a single texture, bind it, enable texture target and use your texture as a texture atlas by using appropriate texture coordinates (see glTexCoords) or create 3 textures for your 3 images and perform enable/bind/draw/disable for each texture.
Have a look at TextureIO, AWTTextureIO, glBindTexture, glEnable, glDisable and glTexCoord2f.
Please note that my answer assumes that you use the fixed pipeline but using the programmable pipeline would be preferable on the long term. You should use the retained mode (VAOs, VBOs, ...) even though you use the fixed pipeline instead of the immediate mode (glBegin, glEnd, glVertex, ...) in my humble opinion.

How to make images on Bitmap smaller?

I am trying to build a simple game on android and while setting the background image and other image assets on my main screen it is appearing too big as shown in the below image.
The actual size of the image is 1920x1080 and I want it to fit my screen like the image shown below:
The code for I have used is:
public class PoliceView extends View {
private Bitmap police;
private Bitmap background;
private Paint scorePaint = new Paint();
private Bitmap life[] = new Bitmap[2];
public PoliceView(Context context) {
super(context);
police = BitmapFactory.decodeResource(getResources(),R.drawable.police);
background = BitmapFactory.decodeResource(getResources(),R.drawable.gamebackground);
scorePaint.setColor(Color.BLACK);
scorePaint.setTextSize(70);
scorePaint.setTypeface(Typeface.DEFAULT_BOLD);
scorePaint.setAntiAlias(true);
life[0] = BitmapFactory.decodeResource(getResources(),R.drawable.heart);
life[1] = BitmapFactory.decodeResource(getResources(),R.drawable.brokenheart);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//The order of draw matters as objects are drawn in this same order
canvas.drawBitmap(background,0,0,null);
canvas.drawBitmap(police, 0, 0, null);
canvas.drawText("Score:",20, 60, scorePaint);
//Three lives for the police
canvas.drawBitmap(life[0],580, 10, null);
canvas.drawBitmap(life[0],680, 10, null);
canvas.drawBitmap(life[0],780, 10, null);
}}
How can I resize the images to fit the screen??
i use this code for resize image
Bitmap tmp = BitmapFactory.decodeFile(mPath);
ResizeWidth = (int) (your size);
ResizeHeight = (int) (your size);
Bitmap bitmap = Bitmap.createBitmap(ResizeWidth, ResizeHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Bitmap scaled = Bitmap.createScaledBitmap(tmp, ResizeWidth, ResizeHeight, true);
int leftOffset = 0;
int topOffset = 0;
canvas.drawBitmap(scaled, leftOffset, topOffset, null);
How to resize bitmap in canvas?
canvas.save(); // Save current canvas params (not only scale)
canvas.scale(scaleX, scaleY);
canvas.restore(); // Restore current canvas params
scale = 1.0f will draw the same visible size bitmap

How to store ImageIcon in Java

I have a matrix n*n of JButton, in a JPanel. Currently, I'm setting ImageIcon in each JButton that change over time. This is not a simple ImageIcon, it's 2 Images that I overlap with this function:
public ImageIcon DoubleImage(BufferedImage eau, BufferedImage img){
// Create a new image.
finalIcon = new BufferedImage(
eau.getWidth(), eau.getHeight(),
BufferedImage.TYPE_INT_ARGB); // start transparent
// Get the graphics object. This is like the canvas you draw on.
Graphics g = finalIcon.getGraphics();
// Now we draw the images.
g.drawImage((Image) eau, 0, 0, null); // start at (0, 0)
img = resize((BufferedImage) img, eau.getWidth(), eau.getHeight());
g.drawImage((Image) img, eau.getWidth()/2-img.getHeight()/2, eau.getHeight()/2-img.getWidth()/2, null); // start at (10, 10)
// Once we're done drawing on the Graphics object, we should
// call dispose() on it to free up memory.
g.dispose();
// Finally, convert to ImageIcon and apply.
ImageIcon icon = new ImageIcon(finalIcon);
return icon;
}
My problem now is that at each iteration of time, I have to change my icons in my JButtons. It implies that I have to redraw the icones while I don't have more than 10 different final images. But it takes too much times (the application lag with a small 10*10 matrix; since the iteration happen every 1 sec, I have to fix this). I had the idea of creating all the Images at the beginning and storing them somewhere, but I don't really know how to perform this? Maybe with an enum? Just in the constructor of my class?
I have to precise that my main class extends JButton, and I instantiate n*n of it for my final matrix.
EDIT: code for the function resize
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
EDIT2: code that I execute at each iteration
public void iteration(){
final Vue vue = marreGraphique.lireVue();
final Presenter presenter = vue.lirePresenter();
try{ //here I'm just instantiating my BufferedImage
eau = ImageIO.read(new File("Icones/mosaique.jpg"));
if(grenouille){
img = ImageIO.read(new File(presenter.getGrenouilleImg()));
}
else{
img = ImageIO.read(new File(presenter.getImg(ligne, colonne)));
}
}
catch (IOException e){}
icon = DoubleImage(eau,img);
setIcon(icon);
setHorizontalAlignment(SwingConstants.CENTER);
setVerticalAlignment(SwingConstants.CENTER);
}
You can put the images in a static, external class (Lets call it Testing for now):
public class Testing {
private static List<ImageIcon> images = new ArrayList<>();
public static void add(ImageIcon im) {
images.add(im);
}
public static List<ImageIcon> get() {
return Testing.images;
}
public static void clear(){
images.clear();
}
...
and then:
icon = DoubleImage(eau,img);
Testing.add(icon);
setIcon(icon);
...
Each time you need to recreate the icons, clear the list with Testing.clear().

Java OpenGL draw offscreen buffer to image

I'm trying to write a java opengl (JOGL) method that writes to an offscreen drawable and then writes that to an image. I have verified this works when using an onscreen drawable as well as GLP buffers, but the output image in its current state is just solid black. The code is below.
GLProfile glp = GLProfile.getDefault();
GLCapabilities caps = new GLCapabilities(glp);
caps.setOnscreen(false);
// create the offscreen drawable
GLDrawableFactory factory = GLDrawableFactory.getFactory(glp);
GLOffscreenAutoDrawable drawable = factory.createOffscreenAutoDrawable(null,caps,null,width,height);
drawable.display();
drawable.getContext().makeCurrent();
// a series of x/y coordinates
FloatBuffer buffer = generateData();
GL2 gl = drawable.getGL().getGL2();
// use pixel coordinates
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(0d, width, height, 0d, -1d, 1d);
// draw some points to the drawable
gl.glPointSize(4f);
gl.glColor3f(1f,0f,0f);
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL2.GL_FLOAT, 0, buffer);
gl.glDrawArrays(GL2.GL_POINTS, 0, numPoints);
BufferedImage im = new AWTGLReadBufferUtil(drawable.getGLProfile(), false).readPixelsToBufferedImage(drawable.getGL(), 0, 0, width, height, true /* awtOrientation */);
ImageIO.write(im,"png",new File("im.png"));
This is a bit old, but I found a solution to the problem that seems to work for me. I just added an ordinary GLEventListener object right before calling .display() on the drawable, as such:
//...
drawable.addGLEventListener(new OffscreenJOGL());
drawable.display();
//Move drawing code to OffscreenJOGL
BufferedImage im = new AWTGLReadBufferUtil(drawable.getGLProfile(), false).readPixelsToBufferedImage(drawable.getGL(), 0, 0, width, height, true /* awtOrientation */);
ImageIO.write(im,"png",new File("im.png"));
The code to draw now should be in your custom OffscreenJOGL class, under the init(...), reshape(...) and display(...) methods. Note that setting the current context must be in the init(...) method of OffscreenJOGL. I get an exception thrown otherwise.
class OffscreenJOGL implements GLEventListener {
public void init(GLAutoDrawable drawable) {
drawable.getContext().makeCurrent();
//Other init code here
}
public void display(GLAutodrawable drawable) {
//Code for drawing here
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
//Called at least once after init(...) and before display(...)
}
public void dispose(GLAutoDrawable drawable) {
//Dispose code here
}
}
Most likely you might have found the required answer for your query.
If not, I suggest to add a line, for example:
gl.glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
I have tested it, and it works.

Overlaying semitransparent layer on a background with Java 3D

I'm using Java3D to visualize a room with some primitives in it. I have an image background that I tile so that it fills the entire frame using background.setImageScaleMode(Background.SCALE_REPEAT);. Now I would like to add another semitransparent background on top of this background, and I would like to stretch it to cover the screen using SCALE_FIT_ALL. This will create an image effect that I cannot otherwise achieve. However, when I try to do this Java 3D complains that Group.addChild: child already has a parent.
Other ways of doing the same thing without using backgrounds (e.g. draw it on a 2D primitive) would be a interest too.
So my question is how can I achieve what I want with Java3D?
MWE: Image are available here. I want to draw bg-stars.png with Background.SCALE_REPEAT and then on top of that bg-glow.png with Background.SCALE_FIT_ALL.
Probably not what you actually want to achieve, but too long for a comment:
I did a test of adding multiple Backgrounds, and it "worked" basically (that is: it did not cause an error message). But the documentation of Background says
If multiple Background nodes are active, the Background node that is "closest" to the eye will be used.
Thus, I assume that it is not possible at all to display multiple backgrounds simulataneously at all.
Depending on what you want to achieve, there are probably several possibilities. The following it one approach that might be "close" to what you want. But I am not familiar with Backgrounds in Java3D, and assume that there are more elegant, efficient, flexible (or simply: better) approaches (like creating a huge, semi-transparent quad with the overlay texture or whatever...)
However, the idea here was to create the background as a single image. Composing BufferedImages is rather easy and offers a lot of possibilities. So I'm taking the bg-stars.png image and create a "tiled" version of this image (large enough to fill a certain area - in practice, this could simply be made as large as the maximum screen size). Then I'm composing this with the "overlay" image, bg-glow.png, by just painting it over the tiled image.
The resulting image can then be used to create the Background.
At the first glance, the result may look like what you want to achieve, but of course, there may be some caveats. E.g. one has to think about how this could be implemented to adapt to changes of the window size. (Listening for this with a ComponentListener and updating the image would be easy, but ... well).
And again: There certainly are better solutions. But maybe this can at least serve as a workaround until you find the better solution.
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
public class SimpleBackgroundTest extends Applet
{
private static final int WIDTH = 1200;
private static final int HEIGHT = 1200;
public static void main(String[] args) throws IOException
{
System.setProperty("sun.awt.noerasebackground", "true");
Frame frame = new MainFrame(new SimpleBackgroundTest(), WIDTH, HEIGHT);
}
public SimpleBackgroundTest()
{
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
add("Center", c);
BranchGroup group = new BranchGroup();
group.addChild(createSomeCube());
BufferedImage stars = null;
BufferedImage glow = null;
try
{
stars = ImageIO.read(new File("bg-stars.png"));
glow = ImageIO.read(new File("bg-glow.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
BufferedImage tiled = createTiled(stars, WIDTH, HEIGHT);
BufferedImage overlay = createOverlay(tiled, glow);
Background background = createBackground(overlay);
group.addChild(background);
SimpleUniverse universe = new SimpleUniverse(c);
universe.addBranchGraph(group);
universe.getViewingPlatform().setNominalViewingTransform();
}
private static BufferedImage createTiled(
BufferedImage image, int targetSizeX, int targetSizeY)
{
BufferedImage result = new BufferedImage(
targetSizeX, targetSizeY,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = result.createGraphics();
for (int x = 0; x < targetSizeX; x += image.getWidth())
{
for (int y = 0; y < targetSizeY; y += image.getHeight())
{
g.drawImage(image, x, y, null);
}
}
g.dispose();
return result;
}
private static BufferedImage createOverlay(
BufferedImage image, BufferedImage overlay)
{
BufferedImage result = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = result.createGraphics();
g.drawImage(image, 0, 0, null);
g.drawImage(overlay, 0, 0, image.getWidth(), image.getHeight(), null);
g.dispose();
return result;
}
private static Background createBackground(BufferedImage image)
{
TextureLoader textureLoader = new TextureLoader(image);
ImageComponent2D imageComponent = textureLoader.getImage();
Background background = new Background();
background.setImage(imageComponent);
background.setImageScaleMode(Background.SCALE_FIT_ALL);
background.setCapability(Background.ALLOW_IMAGE_WRITE);
background.setApplicationBounds(new BoundingSphere());
return background;
}
private TransformGroup createSomeCube()
{
ColorCube cube = new ColorCube(0.5f);
Transform3D t = new Transform3D();
t.rotY(0.2);
t.setScale(0.1);
TransformGroup tg = new TransformGroup();
tg.setTransform(t);
tg.removeAllChildren();
tg.addChild(cube);
return tg;
}
}

Categories

Resources