Search code examples
libgdxscene2d

LibGDX - B/W images render black as transparent and white as black


Long-time reader, first-time poster... I am translating my iOS SpriteKit app to Android using LibGDX and Scene2D. I am slowly getting the hang of it, but I am having a bizarre image issue that I can't wrap my head around. I have three black-and-white images which I use as tiles on my game board. In each case, when I add them to the stage, they render with the black parts transparent, and the white parts black. Here is an example:

black and white image become transparent and black image

Here is my custom class

public class Tile extends Actor {
private int value;
private Image prime;
private Label label;
private boolean isPrime;
private Rectangle r;

Tile(int number, boolean primeSquare, Rectangle rect, float textSize) {
    value = number;
    r = rect;

    if (primeSquare) {
        isPrime = true;
        prime = new Image(new Texture(Gdx.files.internal("prime_background.png")));
        prime.setBounds(r.x, r.y, r.width, r.height);
    } else {
        isAvailable = true;
    }

    Label.LabelStyle style = new Label.LabelStyle(Fonts.bold(textSize), Color.BLACK);
    label = new Label(Integer.toString(number), style);
    label.setBounds(r.x, r.y, r.width, r.height);
    label.setAlignment(Align.center);
}

@Override
public void draw(Batch batch, float parentAlpha) {
    if (isPrime) {
        prime.draw(batch, parentAlpha);
        label.draw(batch, parentAlpha);
    }

}

I have two other b/w images which I have tried in this class as well, and they have the same issue. I substituted a full-colour image and it rendered correctly with no problem.

Is there something special about b/w images that would cause them to render this way? Any other settings that might cause it come out this way that I am overlooking?

It all gets rendered through my custom Screen class, in what I feel is a very ordinary way:

@Override
public void render(float delta) {
    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    stage.act(Gdx.graphics.getDeltaTime());
    stage.draw();
}

Any help would be appreciated. Thanks.

This is the closest thread I have been able to find: LibGDX: Why my black/white picture gets transparent?


Solution

  • A grayscale PNG file might be 8 bit (pure alpha channel) or 16 bit (one color ("luminance") channel and one alpha channel). I don't know which you have, since the texture loading behavior isn't clearly documented, but I'm guessing you saved an 8-bit image. OpenGL will treat this single channel as the alpha (transparency), and add on a color channel that is pure black. This would produce what you're showing in your screen shot.

    Assuming you are drawing this image behind the number, so it doesn't need transparency at all, the simplest solution would be to save your source PNG files with a pure white alpha channel so they are 16 bit images. If that gives you trouble, save them as full color 32-bit PNGs.

    By the way new Texture should not be used in your Actor file. A Texture is a heavy object and also uses native memory that must be cleaned up to prevent memeory leaks. The Java garbage collector does not automatically clean up natively-allocated memory. By creating a new Texture in every Tile of your game, you are loading the same image to memory many times for no reason. And you are leaking the memory if you don't call dispose() on the Texture before the reference is lost.

    Best practice is to have a central assets class where you load all your Textures. You can pass a reference to the same texture to each of your Tile constructors. Your assets class should dispose of all the things it loaded when your game is closed (game.dispose()).

    In fact, LibGDX already has a built-in AssetManager class for this purpose. You can read up on it in the documentation.