Sign in to follow this  
pammatt

[java] byte array heightmap

Recommended Posts

I having a problem understand how Java is storing image data. I read a image I want to use for a heightmap using the ImageIO classes which gives me a BufferedImage of type TYPE_BYTE_GRAY. I get access to the byte array by doing:
byte[] pixels = ((DataBufferByte)img.getRaster().getDataBuffer()).getData();

Now when I print the values of the pixels to the console pixels that are white have a value of -1 and black pixels have a value of 0. This is really confusing to me.. Now I understand Java doesn't have unsigned bytes but I expected pixels that are black to have a value of -128 and pixels that are white to have a value of 127.. but this is definantly not the case. Any suggestions?

Share this post


Link to post
Share on other sites
The values that you got are correct. Why would you assume that you would get the other values?

black is usually always 0, so unsigned or signed it is still 0.

white is usually 255 so unsigned it is still 255 and signed, it is -1. Signed numbers use 2s complement to store numbers.

11111111 = -1
11111110 = -2

Share this post


Link to post
Share on other sites
He probably isn't familiar with 2's complement arithmetic. :/

In Java, bytes are signed. They do not, and cannot represent the 0-255 range (unless you are playing games every time you have to actually do arithmetic with them or display them). They represent the -128 to 127 range.

Now, the greyscale values in the image are expected to be interpreted as signed bytes 0-255, because that's how image formats generally work. Java handles this by just giving you those 8 bits without any translation. So the 0 for black becomes 0 and the 255 for white becomes ... -1.

This is because of how 2's-complement numbers are represented. You can google for it; but basically, the easiest way (IMHO) to think about it is that the most significant bit of a signed byte now means *negative* 128. For example: unsigned byte 255 -> bit pattern 11111111 -> -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 -> signed byte -1.

So now you're probably thinking, "Ack! So now these values I'm getting back will be all out of order! They'll go from 0 to 127, and then -128 to -1! What is a poor Java coder to do?" Well, this is the part where we "play games every time we have to do arithmetic with the value or display it".

The answer is to put those bits into a larger type, so that none of those bits gets interpreted as a sign bit. However, Java also likes to do sign extension when it casts numbers into a larger type (don't ask about char; it gets a bit messy... if you want to know, look up the language spec), so that negative numbers stay representing the same (negative) value.

But, with a bit of understanding of how the numeric representation works (and remember that Java *guarantees* 2s complement arithmetic, along with the size of each numeric type: it is not dependant on the hardware), you can fix that:


byte onos = -86; // 10101010
int biggar = onos; // 11111111111111111111111110101010
// \----------------------/ onos
// sign extension part
// biggar was sign-extended because the msb of onos was set.
// For positive numbers, you'll get 0's in those 24 bits.
int fixored = biggar & 255; // 00000000000000000000000010101010
// \----------------------/ onos
// cleared by bitwise &
// so fixored == 170. For positive numbers this wastes a couple
// of cycles, but is probably better than doing an if - not that
// you should worry. Plus, this is clear code once you're used
// to the idiom.




Of course, you don't really need the temporaries. Since Java implicitly converts things to int all over the place when it does arithmetic, usually it is just sufficient to replace the 'onos' with '(onos & 255)' inside whatever expression you need the unsigned value for. Although you certainly could assign the unsigned value to an int variable, and just use the int later. That would be preferable if you have to use the unsigned value several times in the current bit of code.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by pammatt
You're right, I'm an idiot. I forget the basic things sometimes..

So anyways, I get the unsigned byte value by setting an integer to the value of the signed byte & 0xFF.


Correct.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this