/*
* Copyright (C) 2012 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
* the Free Software Foundation (subject to the "Classpath" exception),
* either version 2, or any later version (collectively, the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.gnu.org/licenses/
* http://www.gnu.org/software/classpath/license.html
*
* or as provided in the LICENSE.txt file that accompanied this code.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bytedeco.javacv;
import com.jogamp.opencl.CLImage2d;
import com.jogamp.opencl.gl.CLGLImage2d;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilitiesImmutable;
import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.Gamma;
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferDouble;
import java.awt.image.DataBufferFloat;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import javax.swing.JFrame;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgcodecs.*;
/**
*
* @author Samuel Audet
*/
public class GLCanvasFrame extends CanvasFrame {
public GLCanvasFrame(String title) {
this(title, 0.0);
}
public GLCanvasFrame(String title, double gamma) {
super(title, gamma);
init(false, null, null);
}
public GLCanvasFrame(String title, GraphicsConfiguration gc,
GLCapabilitiesImmutable caps, GLContext shareWith) {
this(title, gc, caps, shareWith, 0.0);
}
public GLCanvasFrame(String title, GraphicsConfiguration gc,
GLCapabilitiesImmutable caps, GLContext shareWith, double gamma) {
super(title, gc, gamma);
init(false, caps, shareWith);
}
public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode) throws Exception {
this(title, screenNumber, displayMode, 0.0);
}
public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode, double gamma) throws Exception {
super(title, screenNumber, displayMode, gamma);
init(true, null, null);
}
public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode,
GLCapabilitiesImmutable caps, GLContext shareWith) throws Exception {
this(title, screenNumber, displayMode, caps, shareWith, 0.0);
}
public GLCanvasFrame(String title, int screenNumber, DisplayMode displayMode,
GLCapabilitiesImmutable caps, GLContext shareWith, double gamma) throws Exception {
super(title, screenNumber, displayMode, gamma);
init(true, caps, shareWith);
}
private void init(final boolean fullScreen,
final GLCapabilitiesImmutable caps, final GLContext shareWith) {
Runnable r = new Runnable() { public void run() {
String wasErase = System.setProperty("sun.awt.noerasebackground", "true");
canvas = new GLCanvas(caps);
if (shareWith != null) {
((GLCanvas)canvas).setSharedContext(shareWith);
}
((GLCanvas)canvas).addGLEventListener(eventListener);
if (fullScreen) {
canvas.setSize(getSize());
needInitialResize = false;
} else {
canvas.setSize(1, 1); // or we do not get a GLContext
needInitialResize = true;
}
getContentPane().add(canvas);
canvas.setVisible(true);
if (wasErase != null) {
System.setProperty("sun.awt.noerasebackground", wasErase);
} else {
System.clearProperty("sun.awt.noerasebackground");
}
}};
if (EventQueue.isDispatchThread()) {
r.run();
} else {
try {
EventQueue.invokeAndWait(r);
} catch (java.lang.Exception ex) { }
}
}
@Override protected void initCanvas(boolean fullScreen, DisplayMode displayMode, double gamma) { }
private int[] params = new int[2];
private Color color = null;
private int width, height, format, type;
private Buffer buffer = null;
private int frameBuffer = 0, renderBuffer = 0;
private GLEventListener eventListener = new GLEventListener() {
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.setSwapInterval(1); // Sync to VBlank
if (inverseGamma != 1.0) {
// Yeah baby, gamma correction in hardware!
Gamma.setDisplayGamma(drawable, (float)inverseGamma, 0, 1);
}
gl.glGenFramebuffers(1, params, 0);
frameBuffer = params[0];
}
public void dispose(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
params[0] = frameBuffer;
gl.glDeleteFramebuffers(1, params, 0);
if (inverseGamma != 1.0) {
Gamma.resetDisplayGamma(drawable);
}
}
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
if (color != null) {
gl.glClearColor(color.getRed()/255f, color.getGreen()/255f, color.getBlue()/255f, 1f);
gl.glClear(GL2.GL_COLOR_BUFFER_BIT);
} else if (buffer != null) {
if (isResizable() && needInitialResize) {
int w = (int)Math.round(width *initialScale);
int h = (int)Math.round(height*initialScale);
setCanvasSize(w, h);
}
gl.glWindowPos2i(0, canvas.getHeight());
gl.glPixelZoom((float)canvas.getWidth()/width, -(float)canvas.getHeight()/height);
// XXX: Tell OpenGL about the alignment of buffer via glPixelStore(), somehow...
gl.glDrawPixels(width, height, format, type, buffer);
} else if (renderBuffer > 0) {
gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, renderBuffer);
gl.glGetRenderbufferParameteriv(GL2.GL_RENDERBUFFER,
GL2.GL_RENDERBUFFER_WIDTH, params, 0);
gl.glGetRenderbufferParameteriv(GL2.GL_RENDERBUFFER,
GL2.GL_RENDERBUFFER_HEIGHT, params, 1);
if (isResizable() && needInitialResize) {
int w = (int)Math.round(params[0]*initialScale);
int h = (int)Math.round(params[1]*initialScale);
setCanvasSize(w, h);
}
gl.glBindFramebuffer(GL2.GL_READ_FRAMEBUFFER, frameBuffer);
gl.glFramebufferRenderbuffer(GL2.GL_READ_FRAMEBUFFER,
GL2.GL_COLOR_ATTACHMENT0, GL2.GL_RENDERBUFFER, renderBuffer);
// Often GL_RENDERBUFFER_WIDTH == 0 and GL_RENDERBUFFER_HEIGHT == 1,
// while glCheckFramebufferStatus() returns
// GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT here,
// but, for a given object, it either never happens or
// always happens ... NVIDIA driver bug?
//System.out.println(params[0] + " " + params[1] +
// " glCheckFramebufferStatus = " + gl.glCheckFramebufferStatus(GL2.GL_READ_FRAMEBUFFER));
assert gl.glCheckFramebufferStatus(GL2.GL_READ_FRAMEBUFFER) == GL2.GL_FRAMEBUFFER_COMPLETE;
gl.glBlitFramebuffer(0, 0, params[0], params[1],
0, canvas.getHeight(), canvas.getWidth(), 0,
GL2.GL_COLOR_BUFFER_BIT, GL2.GL_LINEAR);
}
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
};
public GLCanvas getGLCanvas() {
return (GLCanvas)canvas;
}
@Override public void showColor(Color color) {
this.color = color;
this.buffer = null;
getGLCanvas().display();
}
@Override public void showImage(Frame frame) {
showImage(frame, false);
}
@Override public void showImage(Frame frame, boolean flipChannels) {
if (flipChannels) {
throw new RuntimeException("GLCanvasFrame does not support channel flipping.");
}
if (frame == null) {
return;
}
this.color = null;
this.width = frame.imageWidth;
this.height = frame.imageHeight;
this.buffer = frame.image[0];
switch (frame.imageDepth) {
case Frame.DEPTH_BYTE: this.type = GL2.GL_BYTE; break;
case Frame.DEPTH_UBYTE: this.type = GL2.GL_UNSIGNED_BYTE; break;
case Frame.DEPTH_SHORT: this.type = GL2.GL_SHORT; break;
case Frame.DEPTH_USHORT: this.type = GL2.GL_UNSIGNED_SHORT; break;
case Frame.DEPTH_INT: this.type = GL2.GL_INT; break;
case Frame.DEPTH_FLOAT: this.type = GL2.GL_FLOAT; break;
case Frame.DEPTH_DOUBLE: this.type = GL2.GL_DOUBLE; break;
default: assert false;
}
switch (frame.imageChannels) {
case 1: this.format = GL2.GL_LUMINANCE; break;
case 2: this.format = GL2.GL_RG; break;
case 3: this.format = GL2.GL_RGB; break;
case 4: this.format = GL2.GL_RGBA; break;
default: assert false;
}
getGLCanvas().display();
}
@Override public void showImage(Image image) {
if (!(image instanceof BufferedImage)) {
throw new RuntimeException("GLCanvasFrame does not support " + image + ", BufferedImage required.");
}
showImage((BufferedImage)image);
}
public void showImage(BufferedImage image) {
if (image == null) {
return;
}
this.color = null;
this.width = image.getWidth();
this.height = image.getHeight();
DataBuffer buffer = image.getRaster().getDataBuffer();
if (buffer instanceof DataBufferByte) {
this.buffer = ByteBuffer.wrap(((DataBufferByte)buffer).getData());
this.type = GL2.GL_UNSIGNED_BYTE;
} else if (buffer instanceof DataBufferDouble) {
this.buffer = DoubleBuffer.wrap(((DataBufferDouble)buffer).getData());
this.type = GL2.GL_DOUBLE;
} else if (buffer instanceof DataBufferFloat) {
this.buffer = FloatBuffer.wrap(((DataBufferFloat)buffer).getData());
this.type = GL2.GL_FLOAT;
} else if (buffer instanceof DataBufferInt) {
this.buffer = IntBuffer.wrap(((DataBufferInt)buffer).getData());
this.type = GL2.GL_INT;
} else if (buffer instanceof DataBufferShort) {
this.buffer = ShortBuffer.wrap(((DataBufferShort)buffer).getData());
this.type = GL2.GL_SHORT;
} else if (buffer instanceof DataBufferUShort) {
this.buffer = ShortBuffer.wrap(((DataBufferUShort)buffer).getData());
this.type = GL2.GL_UNSIGNED_SHORT;
} else {
assert false;
}
switch (image.getSampleModel().getNumBands()) {
case 1: this.format = GL2.GL_LUMINANCE; break;
case 2: this.format = GL2.GL_RG; break;
case 3: this.format = GL2.GL_RGB; break;
case 4: this.format = GL2.GL_RGBA; break;
default: assert false;
}
getGLCanvas().display();
}
public void showImage(int renderBuffer) {
if (renderBuffer <= 0) {
return;
}
this.color = null;
this.buffer = null;
this.renderBuffer = renderBuffer;
getGLCanvas().display();
}
private static GLCanvasFrame canvasFrame;
public static void main(String[] args) throws java.lang.Exception {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
try {
canvasFrame = new GLCanvasFrame("Some Title"/*, context.getGLContext()*/);
canvasFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//canvasFrame.setCanvasSize(640, 480);
canvasFrame.showColor(Color.BLUE);
} catch (java.lang.Exception ex) {
ex.printStackTrace();
}
}
});
final JavaCVCL context = new JavaCVCL(canvasFrame.getGLCanvas().getContext());
final IplImage image = cvLoadImageBGRA("/usr/share/opencv/samples/c/lena.jpg"/*args[0]*/);
//final IplImage image = cvLoadImage("/usr/share/opencv/samples/c/lena.jpg"/*args[0]*/, 0);
//final IplImage image = IplImage.create(640, 480, IPL_DEPTH_32F, 4);
final CLGLImage2d imageCLGL = context.createCLGLImageFrom(image);
//final CLImage2d imageCL = context.createCLImageFrom(image);
context.acquireGLObject(imageCLGL);
context.writeImage(imageCLGL, image, true);
context.releaseGLObject(imageCLGL);
//System.out.println(imageCLGL.getFormat());
//System.exit(0);
canvasFrame.setCanvasScale(0.5);
for (int i = 0; i < 1000; i++) {
canvasFrame.showImage(imageCLGL.getGLObjectID());
Thread.sleep(10);
canvasFrame.showColor(Color.RED);
Thread.sleep(10);
}
canvasFrame.waitKey();
context.release();
System.exit(0);
}
}