Hot questions for Using Lightweight Java Game Library in png

Question:

There's an entity of my LibGDX game I would like to render to a PNG. So I made a small tool that is a LibGDX app to display that entity and it takes a screenshot on F5. The goal of that app is only to generate the PNG.

camera.update();

Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL_COLOR_BUFFER_BIT);

batch.setProjectionMatrix(camera.combined);
batch.begin();

animation.update(Gdx.graphics.getDeltaTime() * 1000);
animation.draw(batch);

batch.end();

if(exporting)
    // export...

From that wiki page I found out how to make a screenshot and by removing the for loop, I was able to get a screenshot that doesn't replace transparent pixels by black pixels.

byte[] pixels = ScreenUtils.getFrameBufferPixels(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), true);

Pixmap pixmap = new Pixmap(Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), Pixmap.Format.RGBA8888);
BufferUtils.copy(pixels, 0, pixmap.getPixels(), pixels.length);
PixmapIO.writePNG(Gdx.files.external("mypixmap.png"), pixmap);
pixmap.dispose();

It works well for the edges of the entity but not for the multiple parts inside.

Edges: (perfect)

Inside: (should not be transparent)

So I started playing with blending to fix that. With

batch.enableBlending();
batch.setBlendFunction(
            exporting ? GL20.GL_ONE : GL20.GL_SRC_ALPHA, // exporting is set to true on the frame where the screenshot is taken
            GL20.GL_ONE_MINUS_SRC_ALPHA);

This improved it a bit:

But with images like glasses that are supposed to be transparent, it's opaque:

Instead of:

Any idea of what I should do to fix this? What I want is pretty standard, a transparent background with semi transparent images on top of it. I want it to behave just like a regular image software would with layers (like GIMP).


Answer:

Your issue is because written colors and alpha are both modulated by same function : SRC_ALPHA and ONE_MINUS_SRC_ALPHA.

You need to use glBlendFuncSeparate to achieve this. In your case :

batch.begin();

// first disable batch blending changes (see javadoc)
batch.setBlendFunction(-1, -1);

// then use special blending.
Gdx.gl.glBlendFuncSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA,GL20.GL_SRC_ALPHA, GL20.GL_DST_ALPHA);

... your drawings ...

batch.end();

In this way, colors channels still blended as usual but alpha channels are added (both source and destination).

Note that with libgdx 1.9.7+, the batch blending hack is not required anymore and could be :

batch.begin();

batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA,GL20.GL_SRC_ALPHA, GL20.GL_DST_ALPHA);

... your drawings ...

batch.end();

Question:

I am making a game with LWJGL and by using openGL, I believe my best option is to use Textures and render them with quads. However, I can only seem to find information on loading a texture from an image where the entire image is only ONE texture. What I would like to do is read an entire spritesheet in and be able to separate it into different textures. Is there a somewhat simple way to do this?


Answer:

You could load the image, from e.g. a .png file to a BufferedImage with

public static BufferedImage loadImage(String location)
{
    try {
        BufferedImage image = ImageIO.read(new File(location));
        return image;
    } catch (IOException e) {
        System.out.println("Could not load texture: " + location);
    }
    return null;
}

Now you are able to call getSubimage(int x, int y, int w, int h) on that resulting BufferedImage, giving you the seperated part. You now just need to create a Texture of the BufferedImage. This code should do the work:

public static int loadTexture(BufferedImage image){
    if (image == null) {
        return 0;
    }

    int[] pixels = new int[image.getWidth() * image.getHeight()];
    image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());

    ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB

    for(int y = 0; y < image.getHeight(); y++){
        for(int x = 0; x < image.getWidth(); x++){
            int pixel = pixels[y * image.getWidth() + x];
            buffer.put((byte) ((pixel >> 16) & 0xFF));     // Red component
            buffer.put((byte) ((pixel >> 8) & 0xFF));      // Green component
            buffer.put((byte) (pixel & 0xFF));               // Blue component
            buffer.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA
        }
    }

    buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS

     // You now have a ByteBuffer filled with the color data of each pixel.
    // Now just create a texture ID and bind it. Then you can load it using 
    // whatever OpenGL method you want, for example:

    int textureID = glGenTextures();
    glBindTexture(GL_TEXTURE_2D, textureID);

    //setup wrap mode
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);

    //setup texture scaling filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    //Send texel data to OpenGL
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); //GL_RGBA8 was GL_RGB8A

    return textureID;
}

You are now able to bind the returned textureID with glBindTexture(GL_TEXTURE_2D, textureID); if you need the texture. This way you only have to split the BufferedImage in the desired parts.

I recommend reading this: LWJGL Textures and Strings

Question:

I am currently programming a game using LWJGL and Java. So far, everything is going well however textures do not seem to be keeping their transparency. At first, I thought I was setting something wrong in OpenGL. However, when I printed out all of the alphas from the BufferedImage being loaded using ImageIO.read(), all of the alphas were 255; meaning there was no transparency even though the PNG file I was loading clearly had transparency in it (I checked the transparency in Paint.NET and made sure I was saving the image correctly and I was, and even verified it with someone else to make sure of it.) As a final check as well, I set the alpha of all of the black pixels manually in the images when loading them to make sure my transparency in OpenGL was working and it was.

I was told by someone that ImageIO.read(), while supporting PNG files, does not support transparency in them and either defaults to an opaque black or white. Is this true? If so, is there another way to load PNG files using Java? (If you would like me to post some code, just let me know through the comments and I will try to edit the answer to only include the code needed.)

EDIT: Per request of MadProgrammer, here is a link to one of the images I am having problems with: https://i.imgur.com/4Vzriem.png The image in question is part of a flickering animation on the menu screen.

EDIT #2: The problem was that when using the Color constructor, the fourth parameter (after the red, green, blue, and alpha) you must specify true in order for the transparency/alpha to be preserved. Otherwise, Java will for God knows what reason discard the given alpha and instead just use 1.0F.


Answer:

So, the short answer is, yes, ImageIO supports PNG transparency in most common PNG formats (I've personally not run it one which doesn't work, but occasionally people post questions stating that it doesn't for a image, but they never post the image for testing).

So, I took you image, dumped into some test code...

import java.awt.Color;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() throws IOException {
            setBackground(Color.RED);
            add(new JLabel(new ImageIcon(ImageIO.read(new File("/Users/shanewhitehead/Downloads/4Vzriem.png")))));
        }

    }

}

Fired up an image editor and compared the results...

So, based on the image editor, the PNG seems to be rendered just fine in Java using ImageIO.

There might be an issue in converting it to a texture in LWJGL however.

For example:

  • LWJGL png texture transparency (textureColour.a white color instead of black)
  • How to draw transparent textures in LWJGL?

Question:

I am trying to display a png as a texture in Eclipse using the LWJGL library. I made sure to bind the texture and to set the coordinates BEFORE drawing the vertexes, but the image still isn't displaying. What could be the problem?

package javagame;

import static org.lwjgl.opengl.GL11.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

@SuppressWarnings("unused")
public class ImageLWJGL {
    public static Texture p;

    public static void main(String[] args) {
        createDisplay();
        createGL();

        render();

        cleanUp();

    }

    private static void createDisplay(){        
        try {
            Display.setDisplayMode(new DisplayMode(800,600));
            Display.create();
            Display.setVSyncEnabled(true);

        } catch (LWJGLException e) {
            e.printStackTrace();
        }
    }

    public static void createGL(){
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();               
        glOrtho(0, Display.getWidth(), 0, Display.getHeight(), -1, 1);      

        glMatrixMode(GL_MODELVIEW);     

        glClearColor(0,0,0,1);          
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_TEXTURE_2D);

    }


    private static  void render(){
        while(!Display.isCloseRequested()){

        try {
             p = TextureLoader.getTexture("PNG", 
                            new FileInputStream(new     File("res/wood.png")));
        } catch (IOException e) {

            e.printStackTrace();
        }



        glClear(GL_COLOR_BUFFER_BIT);
        glLoadIdentity();

        glColor3f(0.25f,0.75f,0.5f);        


        p.bind();

        glBegin(GL_QUADS);

        glTexCoord2f(0,0);
        glVertex2f(0,0);                    // (origin, origin)

        glTexCoord2f(0,1);
        glVertex2f(0,p.getHeight());        // (origin, y-axis)-height

        glTexCoord2f(1,1);
        glVertex2f(p.getWidth(),p.getHeight());     // (x-axis, y-axis)

        glTexCoord2f(1,0);
        glVertex2f(p.getWidth(),0);             // (x-axis, origin)-width


        glEnd();

        Display.update();
        }

    }




private static void cleanUp(){
    Display.destroy();

}

}


Answer:

Your problem is your Ortho Projection Matrix.

glOrtho(0, Display.getWidth(), 0, Display.getHeight(), -1, 1);    

It goes from 0 to Display Width and 0 to Display Height, but you render your Quad from 0 to 1. So your Quad will be rendered, but to Small, that you can see it. To solve the Problem Change the glOrtho to:

glOrtho(0, 1, 0, 1, -1, 1);