In-depth explanation of bufferedimage to bytebuffer?

Since bytebuffers are what opengl uses, and I am needing this for opengl, I figured I’d post here. I’ve seen many pre-made texture loaders and things that convert from bufferedimage to bytebuffer that I could use, but I they’re kind of daunting. What are the absolute basics for what you need changing a bufferedimage to bytebuffer?

For starters, what information does the bytebuffer need to get from bufferedimage and how is it gotten? I know this is a difficult question, sorry. To really understand the question, I feel like starting from the basics would be best.

Look at this post: http://www.java-gaming.org/topics/bufferedimage-to-lwjgl-texture/25516/msg/220280/view.html#msg220280

You are not wrong, although you are focusing too much on the code. You should start by investigating the wonderful world of images, computer monitors and pixels and how a BufferedImage stores them. Pixel formats, bit depths, alpha channels, scanlines, all that jazz.

Thanks for the responses guys,

That’s one of the pre-made ones I was talking about. I suppose I can try to look deeper into it.

Sounds like a good idea. Are there any references or java wiki pages that have something like that I could read, or is it not that straightforward?

Images are extremely straightforward. Quick explanation:

Images are rectangular, made up of width * height number of pixels. Each pixel has 3 or 4 components, R(ed) G(reen) B(lue) are the essential ones and A(lpha) (transparency) if you need it. Most of the time, each component is 1 byte = 256 values (0 to 255), where 0 is none of it and 255 is all of it. When all of them are 0 = the pixel is black, when all of them are 255 = the pixel is white.

There may also be different bit depths, aka the number of bits used to store 1 pixel. The bit depth that I talked about above is 4 components * 8 bits each = 32-bit. There may also be 3 components (no alpha channel) * 8 bits each = 24-bit. Or 4 components * 4 bits each = 16-bit.

You probably already know that an ‘int’ is 32-bits, so…they are mostly used to store pixel data :slight_smile:
You can also use bytes, with every 4 bytes = 1 pixel.

Image file formats, such as PNG, JGP, BMP, etc…, can support different combinations of the components. For example, JPG is a lossy (meaning that the compression algorithm it uses loses data to save space) format that only supports RGB. PNG, however, is a lossless format that supports RGB and A so it is the format most preferred for game textures (however it produces larger files than JPG).

Thank you for typing that. So if images are made of length, width, and the color components, you just transfer that info from the bufferedimage to the bytebuffer and you’re done?

I actually don’t know the difference between a bufferedimage and a bytebuffer, other than OpenGL needs bytebuffers and can’t use bufferedimage. Are there any complications to transfering the length, width, and RGBA?

A BufferedImage is Java2D’s way of storing that data. A ByteBuffer is LWJGL’s way of storing that data to feed to OpenGL.

All ByteBuffer is, is just an array of bytes either in unmanaged (direct) memory or a normal byte[] array.

Thank you for your help, ra4king.

I didn’t know that ByteBuffers were arrays. I looked back at the link that Stranger posted and compared it to a resource and I think I’ve got some of it. Looking at the loadTexture method, I now understand it up to the for-loop.

I don’t understand how the for loop works.

    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
            }

I’ve never heard of some of these things in this loop before. Anyone care to explain it like I’m five? Thank you everybody for your help so far.
Just to show that I’m actually trying to comprehend this and not sitting back and letting you all to do the work, I’ll type what I understand in the loop below.

Say getWidth and getHeight both return 16. The first two lines make x and y go from 0 to 16 in every iteration. The third line is sort of confusing. I think pixel is equal to the certain spot for a pixel in the array that was created earlier? I don’t know how “[y * image.getWidth() + x]” does that though. Then the next 4 lines are just putting data into that pixel? “((byte) ((pixel >> 16) & 0xFF))” is really a piece for me. The reference I was looking at had a ton of different parameter options for put(), and I can’t figure out which one is being used here, so I don’t know what’s happening in there. I’ve seen 0xFF in a book I read a few weeks back but the 0xFF was the reason I put the book down, I have no idea what it is. I don’t know if this is making zero sense at all, I have no idea what’s going on in the bottom 5 lines, just a vague idea not based on the code itself but on context clues.

The pixels array holds pixels in a 1D array, meaning every WIDTH number of indices is 1 scanline, or 1 row of pixels.


pixels[y * image.getWidth() + x]

That gets the pixel at (x,y) by finding the Y’th row and then X’th pixel on that row.

0xFF - this is hex for 255, aka 1 byte: 11111111. I advise you learn the hexadecimal system :slight_smile:

& - that is the AND command. This takes 2 values and outputs an AND’ed value. Basically, it tests each bit in the first value against each bit in the same position in the second value. Both bits have to be 1 to get a 1, otherwise you get 0. For example:


int v1 = 18;       //00001010
int v2 = 20;       //00001100
int o = v1 & v2;   //00001000

There is also the OR command using | : int o = v1 | v2; This tests for at least 1 of the 2 bits to be a 1 to get a 1.
And lastly there is the XOR command using ^ : int o = v1 ^ v2; This tests for at MOST 1 of the 2 bits to be a 1 to get a 1. If both are 1 or 0 then you get 0.

V >> X - this is the right bit shift command. This shifts all the bits in value V to the right X number of times. For example:


byte v = 54;     //00110110 in binary
byte u = v >> 2; //00001101 = value of 13

Since an int is made up of 4 bytes and each color component is 1 byte, you want to separate out those 4 bytes into 4 separate variables.
The first line shifts the bits to the right 16 times to put the 2nd byte from the left in the rightmost byte position and makes sure only those rightmost 8 bits are left by doing “& 0xFF”.
Second line gets the 3rd byte from the left.
Third line just gets the rightmost byte.
Fourth line gets the leftmost byte.

I apologize, it seems like each of your responses are making me ask even more questions. I’m learning a whole lot, though, so thank you again.

If getWidth and getHeight return 4 for the example,
on the first iteration it would be 04 + 0, so it would get the 0th int in the pixels array.
on the second iteration it would be 1
4 + 1, so it would get the 5th int in the pixels array.
on the third iteration it would be 24 + 2, so it would get the 10th int in the pixels array.
on the fourth iteration it would be 3
4 + 3, so it would get the 15th int in the pixels array.
Then y wouldn’t be less than width anymore so it would stop.

Maybe it’s because I don’t understand 1D arrays, but the array was defined with getWidth * getHeight amount of numbers in it, which would be 16. But only the 0th, 5th, 10th, and 15th of them are being used? What about 1-4, 6-9, 11-14?

I went and did just that. Also I found out that the 0x was just the prefix used for hex.

Alright, I think I see what you mean. Using OR in the example you posted would get 0001110, and using XOR would get 0000110.

Oh I get it. It’s stored like AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB, so moving it to the right 16 times would make it AAAAAAAARRRRRRRR, right? And the reason that RRRRRRRR remains and not AAAAAAAARRRRRRRR, is because you ‘AND’ 'd 0xFF, which is 0000000011111111(which, when and’d with something, would make it only use the last 8. )?

[quote]If getWidth and getHeight return 4 for the example,
on the first iteration it would be 04 + 0, so it would get the 0th int in the pixels array.
on the second iteration it would be 1
4 + 1, so it would get the 5th int in the pixels array.
on the third iteration it would be 24 + 2, so it would get the 10th int in the pixels array.
on the fourth iteration it would be 3
4 + 3, so it would get the 15th int in the pixels array.
[/quote]
What do you mean by iteration? What you actually did here was stepping through your image diagonally.
Remember that the pixel at (x,y) is found at data[yw+x], so if you lookup data[24+2] it will be visible at (2,2)

[quote]If getWidth and getHeight return 4 for the example,

Then y wouldn’t be less than width anymore so it would stop.
[/quote]
‘y’ has nothing to do with ‘width’, you made it confusing for yourself by stating that both width and height were 4. Let’s say w=4 and h=7, then y can be greater than w, it just must be less than h.

Everything else is correct :point:

The iteration as in every time it repeats the ‘for’ loop. With the for loops how they are, wouldn’t it just run through it diagonally like you said?

    for(int y = 0; y < image.getHeight(); y++){
        for(int x = 0; x < image.getWidth(); x++){
            int pixel = pixels[y * image.getWidth() + x];
               ...
        }
    }

Ah, nevermind! It doesn’t repeat each ‘for’ loop every time. Instead of what I posted earlier, if w is 4 and h is 7…

0 * 4 + 0 = 0,
0 * 4 + 1 = 1,
0 * 4 + 2 = 2,
0 * 4 + 3 = 3,
THEN the second for loop would end and the first one would iterate with y = 1 and the second one restarts,
1 * 4 + 0 = 4,
1 * 4 + 1 = 5,
1 * 4 + 2 = 6,
1 * 4 + 3 = 7,
Then the second loop ends again, and whatever. It keeps going.

Anyway, I thought that both ‘for’ loops repeated every single time, which wouldn’t make any sense. Thanks for clearing that up, lol.

EDIT: I think I explained that kind of awkwardly. <___<;; heh, ah well.

Glass your replies brought a smile to my face by how quickly you understood it :smiley:

Anyway, now you know how to convert BufferredImage to ByteBuffer :slight_smile:

Gee, I couldn’t have done it without your great help! Thanks.