Well this is just a standalone test, not the project, so there is pretty much nothing else here…
The full code
This is the Frame.java class :
package offscreen;
import java.awt.MouseInfo;
import javax.swing.JFrame;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLJPanel;
public class Frame extends JFrame{
public int mouseX = 0;
public int mouseY = 0;
private static final long serialVersionUID = 1L;
public static GLProfile PROFILE;
public static GLCapabilities CAPABILITIES;
public static GLJPanel panel;
public static PanelListener listener;
public Frame(int width, int height) {
super();
PROFILE = GLProfile.get(GLProfile.GL2);
CAPABILITIES = new GLCapabilities(PROFILE);
CAPABILITIES.setSampleBuffers(true);
CAPABILITIES.setNumSamples(8);
CAPABILITIES.setStencilBits(8);
CAPABILITIES.setDoubleBuffered(true);
CAPABILITIES.setPBuffer(true);
panel = new GLJPanel(CAPABILITIES);
listener = new PanelListener(panel, this);
panel.addGLEventListener(listener);
this.setContentPane(panel);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(width, height);
run();
}
public void run() {
while(true) {
mouseX = MouseInfo.getPointerInfo().getLocation().x - panel.getLocationOnScreen().x;
mouseY = MouseInfo.getPointerInfo().getLocation().y - panel.getLocationOnScreen().y;
panel.display();
try {Thread.sleep(1);}
catch (InterruptedException e) {e.printStackTrace();}
}
}
}
This is the full PanelListener.java class :
package offscreen;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLJPanel;
public class PanelListener implements GLEventListener{
IntBuffer fbo;
IntBuffer fboMSAA;
IntBuffer texture;
IntBuffer textureMSAA;
IntBuffer colorMSAA;
IntBuffer depthMSAA;
Frame frame;
GLJPanel panel;
GL2 gl;
public PanelListener(GLJPanel panel, Frame frame) {
super();
this.panel = panel;
this.frame = frame;
}
@Override
public void display(GLAutoDrawable displayable) {
gl = displayable.getGL().getGL2();
gl.glClearColor(1f, 1f, 1f, 1f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
gl.glColor4f(0f, 0f, 0f, 1f);
gl.glRectd(-1f, 1f, 1f, getYScreen(310));
gl.glColor4f(0f, 0f, 1f, 1f);
gl.glRectd(-1f, getYScreen(310), 1f, getYScreen(340));
gl.glColor4f(0f, 1f, 0f, 1f);
gl.glRectd(-1f, getYScreen(340), 1f, getYScreen(370));
gl.glColor4f(0f, 1f, 1f, 1f);
gl.glRectd(-1f, getYScreen(370), 1f, getYScreen(400));
gl.glColor4f(1f, 0f, 0f, 1f);
gl.glRectd(-1f, getYScreen(400), 1f, getYScreen(430));
gl.glColor4f(1f, 0f, 1f, 1f);
gl.glRectd(-1f, getYScreen(430), 1f, getYScreen(460));
gl.glColor4f(1f, 1f, 0f, 1f);
gl.glRectd(-1f, getYScreen(460), 1f, getYScreen(490));
gl.glColor4f(1f, 1f, 1f, 1f);
gl.glRectd(-1f, getYScreen(490), 1f, -1f);
gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, fboMSAA.get(0));
gl.glViewport(0, 0, 600, 300);
gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glClearColor(0f, 0f, 0f, 0f);
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
float alpha = (float) frame.mouseX / (float) panel.getWidth();
if(alpha > 1f) alpha = 1f;
if(alpha < 0f) alpha = 0f;
alpha = 0.8f;
gl.glColor4f(0f, alpha, 0f, alpha);
gl.glBegin(GL2.GL_TRIANGLE_FAN);
glVertex(150, 150, 600, 300);
for(int i = 0; i < 6; i++) {
float theta = (float) i * 3.1415926f * 4.0f / 5.0f;
glVertex(150 + 120.0 * Math.sin(theta), 150 - 120.0 * Math.cos(theta), 600, 300);
}
gl.glEnd();
List<SVector> polygon = new ArrayList<SVector>();
polygon.add(new SVector(200, 150));
for(int i = 0; i < 6; i++) {
float theta = (float) i * 3.1415926f * 4.0f / 5.0f;
polygon.add(new SVector(200 + 120.0 * Math.sin(theta), 150 - 120.0 * Math.cos(theta)));
}
fillShapeNZ(polygon, 600, 300, true);
gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
gl.glBindFramebuffer(GL2.GL_READ_FRAMEBUFFER, fboMSAA.get(0));
gl.glBindFramebuffer(GL2.GL_DRAW_FRAMEBUFFER, fbo.get(0));
gl.glReadBuffer(GL2.GL_BACK);
gl.glBlitFramebuffer(0, 0, 600, 300, 0, 0, 600, 300, GL2.GL_COLOR_BUFFER_BIT, GL2.GL_LINEAR);
gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
gl.glViewport(0, 0, panel.getWidth(), panel.getHeight());
gl.glColor4f(1f, 1f, 1f, 1f);
gl.glBindTexture(GL2.GL_TEXTURE_2D, texture.get(0));
gl.glBegin(GL2.GL_QUADS);
gl.glTexCoord2f(0, 0);
glVertex(100.5, 250.5);
gl.glTexCoord2f(0, 1);
glVertex(100.5, 550.5);
gl.glTexCoord2f(1, 1);
glVertex(700.5, 550.5);
gl.glTexCoord2f(1, 0);
glVertex(700.5, 250.5);
gl.glEnd();
gl.glBindTexture(GL2.GL_TEXTURE_2D, 0);
gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glColor4f(0f, 1f, 0f, alpha);
gl.glBegin(GL2.GL_TRIANGLE_FAN);
glVertex(550, 400);
for(int i = 0; i < 6; i++) {
float theta = (float) i * 3.1415926f * 4.0f / 5.0f;
glVertex(550 + 120.0 * Math.sin(theta), 400 + 120.0 * Math.cos(theta));
}
gl.glEnd();
polygon = new ArrayList<SVector>();
polygon.add(new SVector(600, 400));
for(int i = 0; i < 6; i++) {
float theta = (float) i * 3.1415926f * 4.0f / 5.0f;
polygon.add(new SVector(600 + 120.0 * Math.sin(theta), 400 + 120.0 * Math.cos(theta)));
}
fillShapeNZ(polygon, panel.getWidth(), panel.getHeight(), false);
int posx = (frame.mouseX - panel.getWidth() / 2) / 10 + 250;
int posy = (frame.mouseY - panel.getHeight() / 2) / 10 + 400;
int color = get(posx, posy);
System.out.println(colorR(color) + " : " + colorG(color) + " : " +colorB(color) + " : " +colorA(color));
int color2 = get(posx - 101, posy - 252, 600, 300, texture.get(0));
System.out.println(colorR(color2) + " : " + colorG(color2) + " : " +colorB(color2) + " : " +colorA(color2));
System.out.println();
gl.glColor4f(1f, 1f, 1f, 1f);
gl.glRectd(getXScreen(posx-0.5), getYScreen(posy-5.5), getXScreen(posx+0.5), getYScreen(posy+4.5));
gl.glRectd(getXScreen(posx-5), getYScreen(posy-1.5), getXScreen(posx+5), getYScreen(posy-0.5));
gl.glColor4f(1f, 1f, 1f, 1f);
gl.glRectd(getXScreen(900), 1f, 1f, -1f);
gl.glColor4f(0.7f, 0.7f, 0.7f, 1f);
gl.glRectd(getXScreen(1000), 1f, getXScreen(1050), -1f);
gl.glColor4f((float) colorR(color) / 255.0f, (float) colorG(color) / 255.0f, (float) colorB(color) / 255.0f, (float) colorA(color) / 255.0f);
gl.glRectd(getXScreen(1000), getYScreen(400), getXScreen(1100), getYScreen(500));
gl.glColor4f((float) colorR(color2) / 255.0f, (float) colorG(color2) / 255.0f, (float) colorB(color2) / 255.0f, (float) colorA(color2) / 255.0f);
gl.glRectd(getXScreen(1000), getYScreen(500), getXScreen(1100), getYScreen(600));
}
public void save(String filename) {
BufferedImage image = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
final int[] a = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
int[] pixels = loadPixels();
System.arraycopy(pixels, 0, a, 0, pixels.length);
File file = new File(filename);
String[] sections = filename.split("\\.");
BufferedImage img;
if(sections[sections.length - 1].equals("jpg") ||
sections[sections.length - 1].equals("jpeg") ||
sections[sections.length - 1].equals("JPG") ||
sections[sections.length - 1].equals("JPEG")) {
img = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB);
img.createGraphics().drawImage(image, 0, 0, null);
}
else if(sections[sections.length - 1].equals("bmp") ||
sections[sections.length - 1].equals("BMP")){
img = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
img.createGraphics().drawImage(image, 0, 0, null);
}
else if(sections[sections.length - 1].equals("wbmp") ||
sections[sections.length - 1].equals("WBMP")){
img = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
img.createGraphics().drawImage(image, 0, 0, null);
}
else {
img = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
img.createGraphics().drawImage(image, 0, 0, null);
}
img.createGraphics().finalize();
try {
ImageIO.write(img, sections[sections.length - 1], file);
} catch (IOException e) {
System.err.println("ERROR : could not save the SImage.");
e.printStackTrace();
}
}
public int[] loadPixels() {
IntBuffer buffer = IntBuffer.allocate(panel.getWidth() * panel.getHeight());
gl.glReadPixels(0, 0, panel.getWidth(), panel.getHeight(), GL2.GL_BGRA, GL2.GL_UNSIGNED_BYTE, buffer);
return buffer.array();
}
public int get(int x, int y) {
IntBuffer buffer = IntBuffer.allocate(1);
gl.glReadPixels(x, panel.getHeight() - y, 1, 1, GL2.GL_BGRA, GL2.GL_UNSIGNED_BYTE, buffer);
return buffer.get(0);
}
public int get(int x, int y, int w, int h, int tex) {
if(x >= 0 && x < w && y >= 0 && y < h) {
gl.glBindTexture(GL.GL_TEXTURE_2D, tex);
IntBuffer pixelRaster = IntBuffer.allocate(w * h);
gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, GL2.GL_BGRA, GL.GL_UNSIGNED_BYTE, pixelRaster);
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
return pixelRaster.get(x + y * w);
}
else return colorRGB(0, 0, 0);
}
@Override
public void dispose(GLAutoDrawable displayable) {
}
@Override
public void init(GLAutoDrawable displayable) {
gl = displayable.getGL().getGL2();
gl.glEnable (GL2.GL_LINE_SMOOTH);
gl.glEnable (GL2.GL_POINT_SMOOTH);
gl.glEnable (GL2.GL_SMOOTH);
gl.glEnable(GL2.GL_TEXTURE_2D);
gl.glDrawBuffer(GL.GL_FRONT_AND_BACK);
gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
gl.glHint(GL2.GL_POINT_SMOOTH_HINT, GL2.GL_NICEST);
gl.glShadeModel(GL2.GL_SMOOTH);
gl.glEnable(GL2.GL_BLEND);
gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glClearColor(0.3f, 0.3f, 0.3f, 1f);
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
gl.glEnable(GL.GL_MULTISAMPLE);
texture = IntBuffer.allocate(1);
fbo = IntBuffer.allocate(1);
textureMSAA = IntBuffer.allocate(1);
fboMSAA = IntBuffer.allocate(1);
colorMSAA = IntBuffer.allocate(1);
depthMSAA = IntBuffer.allocate(1);
//CREATE THE MULTISAMPLED FBO
gl.glGenFramebuffers(1, fboMSAA);
gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, fboMSAA.get(0));
//CREATE THE MULTISAMPLED COLOR RENDERBUFFER
gl.glGenRenderbuffers(1, colorMSAA);
gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, colorMSAA.get(0));
gl.glRenderbufferStorageMultisample(GL2.GL_RENDERBUFFER, 8, GL2.GL_RGBA8, 600, 300);
//BIND THE MULTISAMPLED COLOR RENDERBUFFER TO THE FBO
gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER, GL2.GL_COLOR_ATTACHMENT0, GL2.GL_RENDERBUFFER, colorMSAA.get(0));
// gl.glGenTextures(1,textureMSAA);
// gl.glBindTexture(GL2.GL_TEXTURE_2D_MULTISAMPLE, textureMSAA.get(0));
// gl.glTexStorage2DMultisample(GL2.GL_TEXTURE_2D_MULTISAMPLE, 8, GL2.GL_RGBA8, 600, 300, true);
// gl.glFramebufferTexture2D(GL2.GL_FRAMEBUFFER, GL2.GL_COLOR_ATTACHMENT0, GL2.GL_TEXTURE_2D_MULTISAMPLE, textureMSAA.get(0), 0);
//CREATE THE MULTISAMPLED STENCIL RENDERBUFFER
gl.glGenRenderbuffers(1, depthMSAA);
gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, depthMSAA.get(0));
gl.glRenderbufferStorageMultisample(GL2.GL_RENDERBUFFER, 8, GL2.GL_DEPTH24_STENCIL8, 600, 300);
//BIND THE MULTISAMPLED STENCIL RENDERBUFFER TO THE FBO
gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, fboMSAA.get(0));
gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER, GL2.GL_DEPTH_ATTACHMENT, GL2.GL_RENDERBUFFER, depthMSAA.get(0));
gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER, GL2.GL_STENCIL_ATTACHMENT, GL2.GL_RENDERBUFFER, depthMSAA.get(0));
//CREATE THE CLASSIC FBO
gl.glGenFramebuffers(1, fbo);
gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, fbo.get(0));
//CREATE THE CLASSIC TEXTURE
gl.glGenTextures(1, texture);
gl.glBindTexture(GL2.GL_TEXTURE_2D, texture.get(0));
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL2.GL_TEXTURE_BASE_LEVEL, 0);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAX_LEVEL, 0);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL2.GL_MIRRORED_REPEAT);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL2.GL_MIRRORED_REPEAT);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA8, 600, 300, 0, GL2.GL_BGRA, GL.GL_UNSIGNED_BYTE, null);
//BIND THE CLASSIC TEXTURE TO THE FBO
gl.glFramebufferTexture2D(GL2.GL_FRAMEBUFFER, GL2.GL_COLOR_ATTACHMENT0, GL2.GL_TEXTURE_2D, texture.get(0), 0);
}
@Override
public void reshape(GLAutoDrawable displayable, int x, int y, int w, int h) {
}
public int[] loadPixels(int tex, int w, int h) {
gl.glBindTexture(GL.GL_TEXTURE_2D, tex);
IntBuffer pixelRaster = IntBuffer.allocate(w * h);
gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, GL2.GL_BGRA, GL.GL_UNSIGNED_BYTE, pixelRaster);
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
return pixelRaster.array();
}
public void updatePixels(int tex, int w, int h, int[] pixels) {
gl.glBindTexture(GL.GL_TEXTURE_2D, tex);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, w, h, 0, GL2.GL_BGRA, GL.GL_UNSIGNED_BYTE, IntBuffer.wrap(pixels));
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
}
double getXScreen(double x) {
return 2f * (x + 0.5) / (double) panel.getWidth() - 1f;
}
double getYScreen(double y) {
return 2f * (panel.getHeight() - y - 0.5) / (double) panel.getHeight() - 1f;
}
double getXScreen(double x, double w, double h) {
return 2f * (x + 0.5) / w - 1f;
}
double getYScreen(double y, double w, double h) {
return 2f * (h - y - 0.5) / h - 1f;
}
void fillShapeNZ(List<SVector> polygon, double w, double h, boolean premultiplied) {
if(polygon.size() > 2) {
gl.glClear(GL.GL_STENCIL_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glEnable(GL.GL_STENCIL_TEST);
gl.glDisable(GL.GL_CULL_FACE);
gl.glColorMask(false, false, false, false);
gl.glStencilMask(0xFF);
gl.glStencilFunc(GL.GL_NEVER, 0, 0xFF);
gl.glStencilOpSeparate(GL.GL_BACK, GL.GL_DECR_WRAP, GL.GL_DECR_WRAP, GL.GL_DECR_WRAP);
gl.glStencilOpSeparate(GL.GL_FRONT, GL.GL_INCR_WRAP, GL.GL_INCR_WRAP, GL.GL_INCR_WRAP);
gl.glBegin(GL.GL_TRIANGLE_FAN);
for(SVector v: polygon) glVertex(v.x, v.y, w, h);
gl.glEnd();
gl.glColorMask(true, true, true, true);
gl.glStencilOp(GL.GL_ZERO, GL.GL_ZERO, GL.GL_ZERO);
gl.glStencilFunc(GL.GL_NOTEQUAL, 0, 0xFF);
if(premultiplied) gl.glColor4f(0f, 0f, 0.5f, 0.5f);
else gl.glColor4f(0f, 0f, 1f, 0.5f);
gl.glBegin(GL.GL_TRIANGLE_FAN);
for(SVector v: polygon) glVertex(v.x, v.y, w, h);
gl.glEnd();
gl.glDisable(GL.GL_DEPTH_TEST);
gl.glDisable(GL.GL_STENCIL_TEST);
}
}
private void glVertex(double x, double y) {
gl.glVertex2d(getXScreen(x), getYScreen(y));
}
private void glVertex(double x, double y, double w, double h) {
gl.glVertex2d(getXScreen(x, w, h), getYScreen(y, w, h));
}
static int colorR(int color) {
return (color >> 16) & 0xFF;
}
static int colorG(int color) {
return (color >> 8) & 0xFF;
}
static int colorB(int color) {
return color & 0xFF;
}
static int colorA(int color) {
return (color >> 24) & 0xFF;
}
static int colorRGB(int grey) {
grey = (int) constrain(grey, 0, 255);
return (255 << 24) | (grey << 16) | (grey << 8) | grey;
}
static int colorRGB(int grey, int alpha) {
grey = (int) constrain(grey, 0, 255);
alpha = (int) constrain(alpha, 0, 255);
return (alpha << 24) | (grey << 16) | (grey << 8) | grey;
}
static int colorRGB(int red, int green, int blue) {
red = (int) constrain(red, 0, 255);
green = (int) constrain(green, 0, 255);
blue = (int) constrain(blue, 0, 255);
return (255 << 24) | (red << 16) | (green << 8) | blue;
}
static int colorRGB(int red, int green, int blue, int alpha) {
red = (int) constrain(red, 0, 255);
green = (int) constrain(green, 0, 255);
blue = (int) constrain(blue, 0, 255);
alpha = (int) constrain(alpha, 0, 255);
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
public static double constrain(double x, double a, double b) {
return Math.min(Math.max(x, a), b);
}
}
This is the Main.java class :
package offscreen;
public class Main {
public static void main(String[] args) {
@SuppressWarnings("unused")
Frame frame = new Frame(1280, 720);
}
}
There is also an SVector.java class, taken from the full project, that just contains 3D vectors aswell as a lot of manipulation functions for these, but they are only used as arguments for fillShapeNZ(List, double, double, boolean) so this code should definitely not matter.
There are lots of utility functions, but most of the important stuff is in the Frame constructor, the display function and the init function.
Well, if i retrace everything happening on the panel, first a clear with (1, 1, 1, 1) is performed, then the rectangular color stripes, of fully opaque colors, so at this point the pixel colors should have an alpha of 1 (for example, the magenta should be (1, 0, 1, 1), and I checked with the get(x, y)
function and it is). Then right afterwards, a triangle with color (0, 1, 0, 0.8) is drawn on top of it. The blended color is exactly (0.2, 0.8, 0.2, 1) (or with the 8 bit-per-component colors I’m using, (51, 205, 51, 255)), but on the multisampled edge, the color goes to the (0.66, 0.65, 0.66, 0.89) (or (169, 165, 169, 229) in 8 bit-per-component color).
I should also note that I did try replacing the first clear with (0, 0, 0, 1), and all the colors are exactly the same.
If I was wrong and that the source alpha at this multisampled position wasn’t 0.5, I can solve for it :