Skip to main content
GameDev.net gamedev.net
🔒 Locked

manipulating binary values

Started by Lunpa Sep 15, 2010 at 3:20 PM 25 replies 3.9k views
Original Post
Lunpa
Lunpa
Hello!

The context of the following is storing two 4-bit values in a 8-bit unsigned integer. The integer is a color channel, which is saved into an image file. If I just shift one of the numbers left and OR it to the other, the lower order number is distorted dramatically by image compression, whereas the higher order number is preserved well enough.

Think of the above as storing the binary digits like so: AAAABBBB. Where AAAA and BBBB are 4bit numbers.

What I want to do instead, is store it like ABABABAB. I'd need a very light function to pack the two values together, and another for unpacking.
So, 1010 and 1001 would become 1 1 0 0 1 0 0 1.
The way I'd normally do it is with masking and shifting, eg.



So here is the problem: the unpack function needs to be written in a Cg fragment shader program, with no support for bitwise operators, and a fairly low limit on the number of times in which you can call arithmetic instructions. While the shift operators do not exist, they are simulated easily with multiplication and division.


What I really need is something as simple multiplying either of the four bit numbers by some constant which staggers the bits, and something which I can divide the 8bit number by to produce either of the four bit numbers.



Any thoughts?
rip-off
rip-off
Quote:

If I just shift one of the numbers left and OR it to the other, the lower order number is distorted dramatically by image compression, whereas the higher order number is preserved well enough.

What compression is that? Why are you using lossy compression for this?
Lunpa
Lunpa
Quote:
Original post by rip-off
Quote:

If I just shift one of the numbers left and OR it to the other, the lower order number is distorted dramatically by image compression, whereas the higher order number is preserved well enough.

What compression is that? Why are you using lossy compression for this?


DXT1, and it isn't my choice. The quality loss on its own isn't that big of a deal. I did observe that the 4bit variable on the higher order didn't distort noticeably; however, the lower order one would frequently contain madness when the process was through. So my theory; if the bits are staggered, things will appear to be correct. I'm not looking for precision here... I mean, I'm using 4 bits to store colors instead of 8!

I just want to say, that this whole thing is an experiment that I'd like to see through, and not just give up near the end because it isn't 'proper'.


At any rate, as far as staggering the bits goes; something about it looks irritatingly simple, but at the same time the solution totally escapes me.
alvaro
alvaro
If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.
Lunpa
Lunpa
Quote:
Original post by alvaro
If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.


The idea here is to have a pixel store data for two pixels; an image's dimensions are effectively halved (for every four pixels, two are dropped at pack time, and interpolated from the other two at render time). The nice thing about doing it this way, is if the shader can't be used, the texture can still be used in the normal sort of way, because it is just a lower res version of the original. Think of this as a method of down sampling an image, so that it can be sharpened automatically as it is restored to it's original size.


...

But I'm not looking for a critique the quality of the greater idea at work, I just want to know if anyone has any ideas for the interesting math problem at work here: which is in before using bitwise operators, what is the simplest way to interlace the binary digits of two 4 bit numbers to make an 8 bit number.
alvaro
alvaro
Quote:
Original post by Lunpa
Quote:
Original post by alvaro
If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.


The idea here is to have a pixel store data for two pixels; an image's dimensions are effectively halved (for every four pixels, two are dropped at pack time, and interpolated from the other two at render time). The nice thing about doing it this way, is if the shader can't be used, the texture can still be used in the normal sort of way, because it is just a lower res version of the original. Think of this as a method of down sampling an image, so that it can be sharpened automatically as it is restored to it's original size.


...

But I'm not looking for a critique the quality of the greater idea at work, I just want to know if anyone has any ideas for the interesting math problem at work here: which is in before using bitwise operators, what is the simplest way to interlace the binary digits of two 4 bit numbers to make an 8 bit number.


If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.
Lunpa
Lunpa
Quote:
Original post by alvaro
Quote:
Original post by Lunpa
Quote:
Original post by alvaro
If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.


The idea here is to have a pixel store data for two pixels; an image's dimensions are effectively halved (for every four pixels, two are dropped at pack time, and interpolated from the other two at render time). The nice thing about doing it this way, is if the shader can't be used, the texture can still be used in the normal sort of way, because it is just a lower res version of the original. Think of this as a method of down sampling an image, so that it can be sharpened automatically as it is restored to it's original size.


...

But I'm not looking for a critique the quality of the greater idea at work, I just want to know if anyone has any ideas for the interesting math problem at work here: which is in before using bitwise operators, what is the simplest way to interlace the binary digits of two 4 bit numbers to make an 8 bit number.


If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.


Yes, that would be pretty useful if that was what I'm doing: but that is not the case. I would encourage you to use that method, if you ever need to fiddle with 8 bits at a time. I think it would work really well for you.

Can we talk about math, please?
alvaro
alvaro
Quote:
Original post by Lunpa
So here is the problem: the unpack function needs to be written in a Cg fragment shader program, with no support for bitwise operators, and a fairly low limit on the number of times in which you can call arithmetic instructions.


Quote:
Original post by Alvaro
If you just want to operate on 8 bits at a time, you could make a 256-entry 1-dimensional texture and use it as a look-up table.


Keep reading that sentence until it makes sense. Perhaps you don't know what a lookup table is. But really, that's how I would implement your unpack function.

Lunpa
Lunpa
Quote:
Original post by alvaro
Keep reading that sentence until it makes sense.


Thanks. Condescending remarks are the best way to get others to take a shining to your ideas. Congratulations! You are now the better programmer/ the better mathematician: truly the pinnacle of deep thinking and creativity. No further thought or exploration is necessary when your judicious and wise authority is at hand!

Really, the implementation side of things really isn't all that important; because I can simulate the masking with shifting (divide/multiply by powers of two), and some how I actually can do that in few enough instructions that it actually works. And what do you know? It actually works out pretty well, and I don't have to go back and ditch a week's worth of code, and convince my boss to throw out our 3d engine because it lacks a feature that you're telling me to use.

I understand what you are suggesting, but it leaves a mystery unsolved:

My original question. Is there a better way to interlace two binary numbers when bitwise operators are not available?

At this point, all it really is anymore is an interesting math problem. And if I could free up some time to throw more crap into the shader, that would be nice too, but isn't really strictly necessary either.

[Edited by - Lunpa on September 16, 2010 10:26:11 AM]
alvaro
alvaro
I was honestly only trying to help you. But don't worry, I'll quit trying.

snake5
snake5
Quote:
And guess what, I sincerely agree: it is.

It looks really weird if you agree that something is bad and still don't want to drop your old code and take a better solution.
Ask any good programmer about this and they'll say the same thing - staying with old and barely useful code just because it took some time to write is a bad idea.
Lunpa
Lunpa
Quote:
Original post by snake5
Quote:
And guess what, I sincerely agree: it is.

It looks really weird if you agree that something is bad and still don't want to drop your old code and take a better solution.
Ask any good programmer about this and they'll say the same thing - staying with old and barely useful code just because it took some time to write is a bad idea.


I'm fairly sure I said once before that I don't have full control over the parameters of this project. But if I didn't then I apologize for the confusion.

I think it is fantastic that we're talking so much about math on a math forum. I mean, wouldn't it be weird if it was like pulling teeth to get people to talk about math on a math forum? I think that would be pretty weird.
sooner123
sooner123
You really should go with Alvaro's suggestion. There are only 256 combinations.... (and stop being so critical of other people's advice. especially when they offer it for FREE. so what if it wasn't exactly what you wanted or how you wanted it?)

But as for the math.. I'm not sure I understand the difficulty.

for unpacking, AND the 8-bit with 10101010 and 01010101
snake5
snake5
Ok..
Anyway, alvaro's idea was quite good. I'm sure he meant using a lookup table that is filled on load time with 256 2-dimensional vectors (the output "bits"). You could just put float2 unpacked = Table[ packed ]; (or vec2, I don't really know Cg, just HLSL/GLSL).
Or, if you need constants for something else, you can use an additional texture for the table.
Lunpa
Lunpa
Quote:
Original post by sooner123
You really should go with Alvaro's suggestion. There are only 256 combinations.... (and stop being so critical of other people's advice. especially when they offer it for FREE. so what if it wasn't exactly what you wanted or how you wanted it?)

I would think it were implied by this point by my insistence, but I would like to formally decline Alvaro's advice; for no personal reasons against Alvaro, nor for any objections to the advice's technical merit. May I formally decline? I am certain that I've managed to sully my welcome by a good margin already; but I want to point out that things are offered for "free" quite frequently, and I think there is a place for free will in deciphering one's own needs against what is presented before them.

And besides, I've already solved the original problem in my code, so it really doesn't matter any more anyway. It would be really interesting to know if I could have optimized my method further, but it is academic at this point.

Quote:
Original post by sooner123
But as for the math.. I'm not sure I understand the difficulty.

for unpacking, AND the 8-bit with 10101010 and 01010101


The nature of the problem is that you can not use bitwise operators. There is no AND for floating points. I'm writing this for a hardware profile that does all calculations as floating points: so all binary integer operators are simulated.

For examlpe, if you have a var who's binary value would be 10101010, and you wanted to have the value of the lowest two bits, you could do that like so: fract(var*.25)*4, instead of var&3. I think that the fact that shifting and multiplication/division can be used to accomplish the same thing makes me curious of other patterns that could produce interesting bitwise effects.

So, if there was a simple way to stagger the bits in a number (say, 1111 becomes 10101010 and 1100 becomes 10100000, so on)...

Or really, in a number like a1a1a1a1 to get aaaa, without doing lots of shifting or masking.

So really, all I want to know here (really, please please please) is if there is a simple way to stagger a number like that without using many operators and no bitwise operators. And if that is not how math works, I'm content with that just so long as someone can tell my what that is infeasible.
SiCrane
SiCrane
Quote:
Original post by Lunpa
I would think it were implied by this point by my insistence, but I would like to formally decline Alvaro's advice; for no personal reasons against Alvaro, nor for any objections to the advice's technical merit.

So why? Did you flip a coin and decide that you wouldn't use advice from people who's names contain the letter v? Are you allergic to 1D textures? What other advice will you decide not to listen to without giving any reason whatsoever? Why should anyone give you advice when you have a history of ignoring it without providing an explanation?
rip-off
rip-off
Guys, I think the OP has been perfectly clear since his reply to my question that he does not have direct control of how the textures are uploaded (for whatever reason). Arguing about it won't change that fact.

@OP I can't think of any simple mathematical way of transforming binary values in the way you want. Without bitwise operators, it is difficult to isolate the individual digits to build the result string. Like you said it seems like this is harder than it should be so maybe I'm just missing the obvious too...
SiCrane
SiCrane
Quote:
Original post by rip-off
Guys, I think the OP has been perfectly clear...


Actually, no he hasn't. He's just said "I don't want to use it" without explanation. Where does he say he can't use additional textures?
rip-off
rip-off
Well, maybe not perfectly clear, but the implicit undertone is that he is working with some graphics engine that he cannot modify. I infer from what he says that his options are to change the raw data or change the shaders, but not the code.

If he could upload new textures, then he could do so without DXT1 compression, which invalidates the need for his encoding scheme and hence the thread - I think.
Ryan_001
Ryan_001
He may have been a bit 'snarky' in his responses, but it seemed clear enough to me that he would've used another method if he could. Now on to my answer ;) This is all just 'off the top of my head' so take it for what its worth.

Now instead of focusing on interleaving bits (which kinds breaks down when we have lossy compression) why not just stick with the floats throughout and interleave digits? So say the two values you're trying to store are A and B and are in the range 0 - 0.999999~. Its easy to scale them to that, and for ease we'll assume (or make sure) that they can never be 1. Pseudo-code for breaking them into digits (I'll break each into 2 digits, which would be a range of approximetly 1-100) would be something like:

float a;        // original value, from somewherefloat a2, a1;   // each of the digits of a, a2 being most significanta = modf(a*10.0f,&a2);a = modf(a*10.0f,&a1);// ect... if you want more digits


Now given 4 digits from 2 numbers, interleaving them is simply:

float finalValue = a2*0.1f + b2*0.01f + a1*0.001f + b2*0.0001f;


Now we have a value that can be stored in a single channel of say an 8-bit lossy compressed texture. When uncompressing in the shader code we do basically the 2 same steps over again. Use modf to break the single value into its digits (4 in this case), then reassemble the digits into the 2 final values.

There will be a 'bleeding' between the values due to compression and trying to store two 1-100 ranged values into a single 8-bit byte.

The example used a base of 10 (simply to keep it clear), but you can perform the same steps with any base (2, 4, 8, 16) even bases that aren't powers of 2. The number you pick for your base would depend on how many steps you wanted to take in the shader code, and how much loss of data was acceptable, ect...

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.