LWJGL 3 glfwSetWindowIcon does not work because I didn't use directBuff [solved]

Hello,

I somehow got informed that GLFW 3.2 added support for icons - and that this should work in LWJGL 3.0 too (right?)… I tried to implement it and failed miserably.

Facts: Windows 10 64bit, LWJGL 3.0 (using 64bit Java 1.8, therefore 64bit natives: latest release)

Following code does not work:


    public static void setIcon(long window, BufferedImage img) {
        GLFWImage image = GLFWImage.malloc();
        image.set(img.getWidth(), img.getHeight(), loadImageToByteBuffer(img));

        GLFWImage.Buffer images = GLFWImage.malloc(1);
        images.put(0, image);

        GLFW.glfwSetWindowIcon(window, images);

        images.free();
        image.free();
    }

    private static ByteBuffer loadImageToByteBuffer(final BufferedImage image) {
        final byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4];
        int counter = 0;
        for (int i = 0; i < image.getHeight(); i++) {
            for (int j = 0; j < image.getWidth(); j++) {
                final int c = image.getRGB(j, i);
                buffer[counter + 0] = (byte) (c << 8 >> 24);
                buffer[counter + 1] = (byte) (c << 16 >> 24);
                buffer[counter + 2] = (byte) (c << 24 >> 24);
                buffer[counter + 3] = (byte) (c >> 24);
                counter += 4;
            }
        }
        return ByteBuffer.wrap(buffer);
    }

Note: the loadImageToByteBuffer used to work with LWJGL 2.9.x in some ways, so I had icons on my application prior to the switch to 3.0 :wink:

I already searched around and the setIcon is actually from a stackoverflow (apparently working) answer.

The feedback I get when it crashes is that the LWJGLs checkPointer method from class Checks returns null after the GLFWImage validate method returns 0 as the pointer (although it is called with a pointer thats at least not 0) - this happens in the glfwSetWindowIcon method.

The image I try to set is a 48x48 PNG with alpha and is loaded via ImageIO.

Need help

Thank you very much
Matt_P

You’re using ByteBuffer.wrap in loadImageToByteBuffer. This returns a heap ByteBuffer, which is not supported by LWJGL 3. This post explains why.

Oh, just a small error - phew, thanks a lot!
I’ll try this once I’m at home again :slight_smile:

Edit: Read the post now, good to know - I will be a good buy and only use the BufferUtils from now on :slight_smile:

Thanks again!

You don’t necessarily need to use BufferUtils, just use BytrBuffer.allocateDirect then use the put method of the ByteBuffer to load the array data in to the buffer. BufferUtils is a useful utility method but it hides things you should really know about.

[quote=“ziozio,post:4,topic:57357”]
BufferUtils doesn’t hide anything. It uses ByteBuffer.allocateDirect and sets the byte order to ByteOrder.nativeOrder(). Unless you’re doing something very specialized (e.g. low-level networking), using the native byte order is exactly what you want. Actually, not setting the byte order has always been one of the most common issues new LWJGL users face.

As for allocateDirect itself, the post explains why avoiding it is a good idea. In LWJGL code I write, this is my decision process (from most-to-least preferred):

  1. Is it a short buffer/struct that is created, used and discarded locally*? Use MemoryStack for stack allocations.
  2. Is it trivial to manually free the buffer/struct? Or, is the allocation in a performance sensitive path? Use MemoryUtil for the explicit alloc/free functionality.
  3. Use BufferUtils.
  4. Use ByteBuffer.allocateDirect() directly.
  • “Local” could apply to any allocation with a lifetime of a single method up to a single frame (e.g. in the scope of the main loop).

No worries, I checked the Source of BufferUtils and found out that the methods I use now do exactly what I would have done manually.

Since I’m home now I changed the code and now everything works as expected.

Thanks again Spasi!

If someone with the necessary power reads this: You can close this ticket if you want to, as it solved :wink:

This is not how this forum works :slight_smile: What if somebody wanted to add some other piece of wisdom? (Or share a good joke about… ehm… buffers!)

You can put [solved] in the subject-line though, the forum will turn it into a pretty little picture.

Can I please get further help with this? I believe I’m understanding all of this and my situation is the exact same as OP. I can’t find a better answer for how to use glfwSetWindowIcon anywhere else online so far. I read the article Spasi linked, and I believe I understand needing direct vs heap, but it didn’t set me straight yet I guess. If I could just see the corrections OP made, that would be perfect (if it works for me too :stuck_out_tongue: )

I have altered OP’s code to,

    public static void setIcon(long window, BufferedImage img) {
        GLFWImage image = GLFWImage.malloc();
        image.set(img.getWidth(), img.getHeight(), loadImageToByteBuffer(img));

        GLFWImage.Buffer images = GLFWImage.malloc(1);
        images.put(0, image);
        
        GLFW.glfwSetWindowIcon(window, images);


        images.free();
        image.free();
    }

    private static ByteBuffer loadImageToByteBuffer(final BufferedImage image) {
        final byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4];
        int counter = 0;
        for (int i = 0; i < image.getHeight(); i++) {
            for (int j = 0; j < image.getWidth(); j++) {
                final int c = image.getRGB(j, i);
                buffer[counter + 0] = (byte) (c << 8 >> 24);
                buffer[counter + 1] = (byte) (c << 16 >> 24);
                buffer[counter + 2] = (byte) (c << 24 >> 24);
                buffer[counter + 3] = (byte) (c >> 24);
                counter += 4;
            }
        }
        ByteBuffer bb = BufferUtils.createByteBuffer(buffer.length);
        bb.put(buffer);
        return bb;
    }

but I still get the same error OP reports (java.lang.NullPointerException
at org.lwjgl.system.Checks.checkPointer(Checks.java:103)). The image I’m trying to load is 48x48 png.

I can start a new thread if that is preferable, but it seems very relevant to this post already. Thank you!

One obvious bug with the above code is that the ByteBuffer is not flipped after the put call. Other than that, I cannot reproduce the NPE and the code works fine for me. Could you post the entire stacktrace?

More observations:

  • GLFWImage.Buffer is an array of GLFWImage structs. You don’t need to allocate a GLFWImage, then copy it to the buffer. The buffer can be used as a flyweight, it exposes all methods GLFWImage has and it applies them to the current buffer position.
  • In loadImageToByteBuffer, you don’t need to allocate a byte[], fill it, then copy it to the ByteBuffer. You can fill the ByteBuffer directly and avoid the overhead.
  • LWJGL comes with bindings to stb_image, which can be used to load a PNG file directly as a ByteBuffer. It is faster and more memory efficient than using ImageIO and BufferedImages.

This is what I would use:

try ( MemoryStack stack = stackPush() ) {
	IntBuffer w = stack.mallocInt(1);
	IntBuffer h = stack.mallocInt(1);
	IntBuffer comp = stack.mallocInt(1);

	ByteBuffer icon = stbi_load("modules/core/src/test/resources/lwjgl32.png", w, h, comp, 4);

	glfwSetWindowIcon(window, GLFWImage.mallocStack(1, stack)
		.width(w.get(0))
		.height(h.get(0))
		.pixels(icon)
	);

	stbi_image_free(icon);
}

Spasi, your response is too awesome! Concise, clear, and thorough. Suffice it to say, the code block you recommend at the end works for me! Thank you so much. Really can’t thank you enough for such a quick response with exactly what I needed.

Regarding the rest of your advice, the truth is, I’m very green with OGL and LWJGL. I understand the code I see, but I’ve had a very hard time finding the right information to learn from. Thus you see some of the flaws in my code I linked. I will go through the points you made, but for the purpose of this message board, that seems irrelevant. Just me learning what most people who do this probably already know.

For instance, the main project I’m working on, I somehow built wrong? It doesn’t find the stb image bindings or stackpush(). Only a different, bigger project has those. Now I know I need to import those and what to import, but time and time again, it is the same situation of an easy answer made hard through my ignorance and scarce info. If I can get good, I’d almost consider putting together a Dummy’s Guide to OpenGL/LWJGL just because I don’t want people to struggle like me. It’s quite interesting/powerful once you get the hang of it.

I can post any further info if you still wish it, Spasi, otherwise, thank you so much again! :smiley:

[quote=“SolidGold,post:10,topic:57357”]
We recognize that this has always been an issue for new LWJGL users and will try to do something about it in the future. (see LWJGL#201)

In the meantime, my recommendation to new users is that they go through the sample code and demo suite.