Taking a screenshot with JOGL - java

I am looking for a way to screenshot my GLCanvas programmatically without awt Robot.
Here is my current setup:
Constructor:
glcaps = new GLCapabilities(GLProfile.get(GLProfile.GL2));
glcaps.setDoubleBuffered(true);
glcaps.setHardwareAccelerated(true);
glcanvas = new GLCanvas(glcaps);
glcanvas.setSize(720, 720);
glcanvas.addGLEventListener(this);
glcanvas is declared as an instance variable: GLCanvas glcanvas
OpenGL init:
#Override
public void init(GLAutoDrawable glad) {
GL2 gl = glad.getGL().getGL2();
glu = new GLU();
gl.glEnable(GL2.GL_DEPTH_TEST);
gl.glDepthFunc(GL2.GL_LEQUAL);
gl.glShadeModel(GL2.GL_SMOOTH);
gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
gl.glClearColor(0f, 0f, 0f, 1f);
// Some camera related code not shown
}
OpenGL display:
public void display(GLAutoDrawable glad) {
GL2 gl = glad.getGL().getGL2();
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
...
// Orient camera and draw a simple cube
...
gl.glFlush();
}
Screenshot method:
BufferedImage b = new BufferedImage(glcanvas.getWidth(), glcanvas.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = b.createGraphics();
glcanvas.setupPrint(glcanvas.getWidth(), glcanvas.getWidth(), 50, 50, 50);
glcanvas.print(g);
try {
ImageIO.write(b, "png", new File("test.png"));
} catch (IOException ex) {
// Error handling
}
glcanvas.releasePrint();
g.dispose();
This method works, as in executes without crashing, but the png file I get is just black with no cube. I also tried using glReadPixels but that does not work either as it just gives me a buffer full of 0's (black).
I think that the problem is that I am not reading glcanvas from the draw thread. Is this the error, and if so, how can I solve it?
All answers appreciated!

First, you have to be sure that you read the framebuffer after what you want to catch has been rendered.
Second, you can do something like this:
protected void saveImage(GL3 gl3, int width, int height) {
try {
BufferedImage screenshot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = screenshot.getGraphics();
ByteBuffer buffer = GLBuffers.newDirectByteBuffer(width * height * 4);
// be sure you are reading from the right fbo (here is supposed to be the default one)
// bind the right buffer to read from
gl3.glReadBuffer(GL_BACK);
// if the width is not multiple of 4, set unpackPixel = 1
gl3.glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
// The color are the three consecutive bytes, it's like referencing
// to the next consecutive array elements, so we got red, green, blue..
// red, green, blue, and so on..+ ", "
graphics.setColor(new Color((buffer.get() & 0xff), (buffer.get() & 0xff),
(buffer.get() & 0xff)));
buffer.get(); // consume alpha
graphics.drawRect(w, height - h, 1, 1); // height - h is for flipping the image
}
}
// This is one util of mine, it make sure you clean the direct buffer
BufferUtils.destroyDirectBuffer(buffer);
File outputfile = new File("D:\\Downloads\\texture.png");
ImageIO.write(screenshot, "png", outputfile);
} catch (IOException ex) {
}
}
I filled some comment inside, if something is still unclear, don't hesitate to ask further

Related

cropping in image into an cricle

I want to crop an image which is rectangular , into a circle of specific diameter. I am able to do it through graphics2D, and I get the image saved, but, when I read it through ImagIO, i get the full image again inspite to it being cropped to a circle. the image is a masked circle, and evrything outside is discarded like a mask. I am attaching the image here. inspite of it being clipped, i get the full image rendered, when i read it through imageIO. here is the code.
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
Area areaOval = new Area(new Arc2D.Double(0, 0, w, w, 0, 360,
Arc2D.PIE));
Shape shapeClipSave = g2.getClip();
g2.setClip(areaOval);
g2.drawImage(bufferedImage, 0, 0, null);
g2.setClip(shapeClipSave);
bufferedImage=output;
try {
ImageIO.write(bufferedImage,"png", new File("D:/new.png"));
bufferedImage= ImageIO.read(new File("D:/new.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g2.dispose();
Here's my take. I rewrote some parts for performance and better fidelity (I couldn't get the edges of the circular area antialiased using clip). Although your code should also work, in general.
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
// Remove odd borders (imgur issue?)... Remove this if your input doesn't have borders
image = image.getSubimage(10, 0, image.getWidth() - 20, image.getHeight() - 10);
int w = image.getWidth();
int h = image.getHeight();
image = createCircular(image, Math.min(w, h));
if (!ImageIO.write(image, "png", new File("new.png"))) {
System.err.println("Could not write PNG format");
System.exit(1);
}
image = ImageIO.read(new File("new.png"));
showItAll(image);
}
private static BufferedImage createCircular(BufferedImage image, int size) {
BufferedImage output = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.fillOval(0, 0, size, size);
g2.setComposite(AlphaComposite.SrcIn);
g2.drawImage(image, 0, 0, null);
}
finally {
g2.dispose();
}
return output;
}
private static void showItAll(BufferedImage image) {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(new JPanel() {
{
setBackground(Color.ORANGE);
}
});
frame.getContentPane().add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
Using your giraffe as input, I got the following output, the orange background is just to make the transparent parts clearly visible:
Alternatively, if you use the TwelveMonkeys library and the Adobe Path Support module, you can replace:
image = createCircular(image, Math.min(w, h));
with the following:
int size = Math.min(w, h);
image = Paths.applyClippingPath(new Ellipse2D.Float(0, 0, 1, 1),
image.getSubimage(0, 0, size, size));
Just be aware that the shape coordinates are relative to the size of the image, not in pixels.

How to Delete the white pixels in an image in java

How can I remove the white pixels of an image before loading it in the Panel
the method for loading image in panel is:
public void ajouterImage(File fichierImage) {
// desiiner une image à l'ecran
try {
monImage = ImageIO.read(fichierImage);
} catch (IOException e) {
e.printStackTrace();
}
repaint();
}
You can't remove a pixel for say from an image, but you sure can change the color of it, or even make it transparent.
Let's say you have a pixel array as a variable somewhere, and you can give it the RGB value of your BufferedImage. The pixel array will be called pixels:
try {
monImage = ImageIO.read(fichierImage);
int width = monImage.getWidth();
int height = monImage.getHeight();
pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
// I used capital F's to indicate that it's the alpha value.
if (pixels[i] == 0xFFffffff) {
// We'll set the alpha value to 0 for to make it fully transparent.
pixels[i] = 0x00ffffff;
}
}
} catch (IOException e) {
e.printStackTrace();
}
Assuming that by removing pixels you mean setting them to transparent, you need to set the alpha value of the image to zero. Here is a function colorToAlpha(BufferedImage, Color) which takes a BufferedImage and a Color as input and returns another BufferedImage with the Color set to transparent.
public static BufferedImage colorToAlpha(BufferedImage raw, Color remove)
{
int WIDTH = raw.getWidth();
int HEIGHT = raw.getHeight();
BufferedImage image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_ARGB);
int pixels[]=new int[WIDTH*HEIGHT];
raw.getRGB(0, 0, WIDTH, HEIGHT, pixels, 0, WIDTH);
for(int i=0; i<pixels.length;i++)
{
if (pixels[i] == remove.getRGB())
{
pixels[i] = 0x00ffffff;
}
}
image.setRGB(0, 0, WIDTH, HEIGHT, pixels, 0, WIDTH);
return image;
}
Example usage:
BufferedImage processed = colorToAlpha(rawImage, Color.WHITE)

Shift Image while keeping dimensions

I have an image that I would like to shift by some x, y value and then save. My problem is that I would like to keep my original dimensions, so that there is x and y "blank" space left after shifting my image.
Also, is there any way I can set the "blank" space to black?
Example: I shift a 600x600 image down by 45 and left by 30, so that the image is still 600x600 but the result has a 45 height and 30 width of "blank" space.
So far I have been using getSubimage method of BufferedImage to try and solve this but I cannot seem to return to the original dimensions.
Any ideas on how to solve this problem?
You could do that by creating a new buffered image and drawing on to it.
// Create new buffered image
BufferedImage shifted = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Create the graphics
Graphics2D g = shifted.createGraphics();
// Draw original with shifted coordinates
g.drawImage(original, shiftx, shifty, null);
Hope this works.
public BufferedImage shiftImage(BufferedImage original, int x, int y) {
BufferedImage result = new BufferedImage(original.getWidth() + x,
original.getHeight() + y, original.getType());
Graphics2D g2d = result.createGraphics();
g2d.drawImage(original, x, y, null);
return result;
}
Should work.
Saving
public void SaveImage(BufferedImage image, String filename) {
File outputfile = new File(filename + ".png");
try {
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Removing BufferedImage pixel values and or setting them transparent

I have been working with the polygon class and trying to set the pixel values inside of the polygon to transparent or remove them all together if this is possible, however I have hit a bit of a wall as I am trying to store the values as RGB int values and don't know how I would be able to make a pixel transparent/removed via this method.
Additionally to this I would also like to do the same thing but keeping pixels inside the polygon and deleting those outside if possible in order to be left with only the pixels contained within the polygon. I have searched around for this before but to no avail.
I did attempt to create a SSCCE for this to make it easier to work with and view for anyone taking the time to help however as its part of a much larger programme that I am working on creating one is proving to take some time, however once I have one working to better demonstrate this problem I will edit this post.
Thank you to anyone for taking the time to help me with this problem
Below I have some code for what I am currently using to segment the pixels that are contained within an already specified polygon. This is extremely similar to the way i do it for setting pixels outside the polygon to transparent only with the if statement arguments swapped around to remove a segment of the image and haveing a return for newImage rather than save image stuff and it works perfectly, however when I do it this way to save the pixels contained in the polygon it doesn't save for some reason.
public void saveSegment(int tabNum, BufferedImage img) {
segmentation = new GUI.Segmentation();
Polygon p = new Polygon();
Color pixel;
p = createPolygon(segmentation);
int height = img.getHeight();
int width = img.getWidth();
newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//loop through the image to fill the 2d array up with the segmented pixels
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
//If the pixel is inside polygon
if(p.contains(x, y) == true) {
pixel = new Color(img.getRGB(x, y));
//set pixel equal to the RGB value of the pixel being looked at
int r = pixel.getRed(); // red component 0...255
int g = pixel.getGreen(); // green component 0...255
int b = pixel.getBlue(); // blue component 0...255
int a = pixel.getAlpha(); // alpha (transparency) component 0...255
int col = (a << 24) | (r << 16) | (g << 8) | b;
newImage.setRGB(x, y, col);
}
else {
pixel = new Color(img.getRGB(x, y));
int a = 0; // alpha (transparency) component 0...255
int col = (a << 24);
newImage.setRGB(x, y, col);
}
}
}
try {
//then save as image once all in correct order
ImageIO.write(newImage, "bmp", new File("saved-Segment.bmp"));
JOptionPane.showMessageDialog(null, "New image saved successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
An easier way is to use Java2D's clipping capability:
BufferedImage cutHole(BufferedImage image, Polygon holeShape) {
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(), image.getType());
Graphics2D g = newImage.createGraphics();
Rectangle entireImage =
new Rectangle(image.getWidth(), image.getHeight());
Area clip = new Area(entireImage);
clip.subtract(new Area(holeShape));
g.clip(clip);
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
BufferedImage clipToPolygon(BufferedImage image, Polygon polygon) {
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(), image.getType());
Graphics2D g = newImage.createGraphics();
g.clip(polygon);
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}

Display image in JOGL

I'm currently working on an assignment where I need to create a game in JOGL, it's going pretty well, but I've run into a problem:
I want to create a menu function that can be accessed by pressing ESC ingame, when ESC is pressed the display function needs to stop displaying the game and start displaying the menu. The menu consists of a background image with some text overlay.
This is how I tried to implement the menu function, but I haven't managed to let it show anything else than the clear color:
public class OptionsMenu
{
public void display(GL gl) {
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(0, 300, 300, 0, 0, 1);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glDisable(GL.GL_DEPTH_TEST);
gl.glClearColor(1f, 0.5f, 0.5f, 0.5f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glBlendFunc (GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnable (GL.GL_BLEND);
BufferedImage bufferedImage = null;
int w = 0;
int h = 0;
try {
bufferedImage = ImageIO.read(OptionsMenu.class.getResource("menuBackground.jpg")); //The menu background
w = ceilingPow2(bufferedImage.getWidth());
h = ceilingPow2(bufferedImage.getHeight());
} catch (IOException e) {
e.printStackTrace();
}
WritableRaster raster =
Raster.createInterleavedRaster (DataBuffer.TYPE_BYTE,
w,
h,
4,
null);
ComponentColorModel colorModel=
new ComponentColorModel (ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8,8,8,8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
BufferedImage img =
new BufferedImage (colorModel,
raster,
false,
null);
Graphics2D g = img.createGraphics();
g.drawImage(bufferedImage, null, null);
DataBufferByte imgBuf =
(DataBufferByte)raster.getDataBuffer();
byte[] imgRGBA = imgBuf.getData();
ByteBuffer bb = ByteBuffer.wrap(imgRGBA);
bb.position(0);
bb.mark();
gl.glBindTexture(GL.GL_TEXTURE_2D, 13);
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
gl.glTexImage2D (GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, w, h, 0, GL.GL_RGBA,
GL.GL_UNSIGNED_BYTE, bb);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glBindTexture (GL.GL_TEXTURE_2D, 13);
gl.glBegin (GL.GL_POLYGON);
gl.glTexCoord2d (0, 0);
gl.glVertex2d (0, 0);
gl.glTexCoord2d(1,0);
gl.glVertex2d (w, 0);
gl.glTexCoord2d(1,1);
gl.glVertex2d (w, h);
gl.glTexCoord2d(0,1);
gl.glVertex2d (0, h);
gl.glEnd ();
gl.glFlush();
}
private static int ceilingPow2(int n) {
int pow2 = 1;
while (n > pow2) {
pow2 = pow2<<1;
}
return pow2;
}
}
This code is based of this tutorial: http://wiki.tankaar.com/index.php?title=Displaying_an_Image_in_JOGL_(Part_1)
I'm calling upon the OptionsMenu like so:
public void display(GLAutoDrawable drawable)
{
GL gl = drawable.getGL();
GLU glu = new GLU();
if(state.equals("optionsMenu"))
{
if(menu == null)
menu = new OptionsMenu;
menu.display(gl);
}
else
{
// Calculating time since last frame.
Calendar now = Calendar.getInstance();
long currentTime = now.getTimeInMillis();
int deltaTime = (int)(currentTime - previousTime);
previousTime = currentTime;
// Update any movement since last frame.
updateMovement(deltaTime);
updateCamera();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
gl.glLoadIdentity();
glu.gluLookAt( camera.getLocationX(), camera.getLocationY(), camera.getLocationZ(),
camera.getVrpX(), camera.getVrpY(), camera.getVrpZ(),
camera.getVuvX(), camera.getVuvY(), camera.getVuvZ() );
// Display all the visible objects of MazeRunner.
for( Iterator<VisibleObject> it = visibleObjects.iterator(); it.hasNext(); ) {
it.next().display(gl);
}
gl.glLoadIdentity();
gl.glFlush();
}
}
It doesn't throw any errors, it just won't display the image. It does however show the clear color gl.glClearColor(1f, 0.5f, 0.5f, 0.5f); defined in the display function of the OptionsMenu
I'm pretty much stumped and I have no idea how I'm going to fix this.
Sorry for the long post, but I'd be REALLY grateful if someone would help me.
I guess clear color buffers gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
should be placed at the begining of display method of OptionsMenu.
I have few advices for example posted code above:
Change scope of the GLU glu = new GLU();, because glu assigned each time when opengl display callback method called.
Also move gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); right before if statement, and do not use every objects display method (e.g OptionsMenu.display()). Try keep it single.
Use TextureIO to upload texture, and if its possible just upload it once then use that texture in your display method. Texture loading process has great overhead.

Categories