Byte arithmetic operations for color calculations
In order to save memory when making a color class i'm using unsigned chars to represent colors. This is all nice and all but when it comes to calculations i run into lots of trouble.
First thing: Addition wraps around the 255 max value of a byte. Can anyone help me with a function that will add and saturate the color? How about subtraction?
Second thing: Can i do fast multiplications with a float value (from 0.0 to 1.0) without having to convert the byte to float? How about rounding the results (64 x 0.7 = 44.8 -> should be 45 but the conversion back to byte makes it 44).
You can use a fixed point representation instead of floating-point and avoid a byte-to-float conversion, but you'll lose some accuracy.
Obviously the use of the Fixed datatype in this example offers no benefit, since a new Fixed object is created from a float each time, but if you created one Fixed object and used it multiple times you'd see a (small) benefit.
Enigma
#include <cmath>#include <iostream>typedef unsigned char byte;class Fixed{ public: Fixed(float value) { value_ = 255 * std::min(std::max(0.0f, value), 1.0f); } byte mult(byte value) { unsigned short result = (((unsigned short)(value_)) * value) + 128; return result / 255; } private: byte value_;};byte saturatedAdd(byte a, byte b){ byte result = a + b; if (result < a || result < b) { return 255; } return result;}byte saturatedSubtract(byte a, byte b){ byte result = a - b; if (result > a) { return 0; } return result;}byte multiplyByteFloat(byte a, float b){ return (a * std::min(std::max(0.0f, b), 1.0f)) + 0.5f;}byte multiplyByteFixed(byte a, Fixed b){ return b.mult(a);};int main(){ std::cout << int(saturatedAdd(35, 79)) << '\n'; std::cout << int(saturatedAdd(255, 255)) << '\n'; std::cout << int(saturatedAdd(1, 255)) << '\n'; std::cout << int(saturatedAdd(134, 207)) << '\n'; std::cout << int(saturatedSubtract(79, 35)) << '\n'; std::cout << int(saturatedSubtract(255, 255)) << '\n'; std::cout << int(saturatedSubtract(0, 255)) << '\n'; std::cout << int(saturatedSubtract(1, 255)) << '\n'; std::cout << int(saturatedSubtract(134, 207)) << '\n'; std::cout << int(multiplyByteFloat(35, 0.5)) << '\n'; std::cout << int(multiplyByteFloat(255, 1)) << '\n'; std::cout << int(multiplyByteFloat(255, 0.55)) << '\n'; std::cout << int(multiplyByteFloat(0, 1)) << '\n'; std::cout << int(multiplyByteFixed(35, 0.5)) << '\n'; std::cout << int(multiplyByteFixed(255, 1)) << '\n'; std::cout << int(multiplyByteFixed(255, 0.55)) << '\n'; std::cout << int(multiplyByteFixed(0, 1)) << '\n';}
Obviously the use of the Fixed datatype in this example offers no benefit, since a new Fixed object is created from a float each time, but if you created one Fixed object and used it multiple times you'd see a (small) benefit.
Enigma
Take my advice: Ditch the floats. Instead of passing in floats between zero and 1, pass in integers (or bytes) from 0 to 255. And I don't mean just converting it before calling the function. I mean actually use entirely integral data types (fixed point) instead of any real numbers.
Here's a few optimised C routines I've used in the past:
Here's a few optimised C routines I've used in the past:
//(be warned, these are potentially unportable so I'd double // check that they work properly on any other compiler)inline int clipMin(int value, const int minVal = 0) { return (minVal & (-(int)(value < minVal))) | (value & (-(int)!(value < minVal)));}inline int clipMax(int value, const int maxVal = 255) { return (maxVal & (-(int)(value > maxVal))) | (value & (-(int)!(value > maxVal)));}// color c1 += c2 * alphainline UINT32 AdditiveBlend32(unsigned long c1, unsigned long c2, unsigned long alpha) { INT32 a1 = (alpha)+1; return (clipMax(((c1>>16)&0xFF) + ((((c2>>16)&0xFF)*a1)>>8), 0xFF)<<16) + (clipMax(((c1>> 8)&0xFF) + ((((c2>> 8)&0xFF)*a1)>>8), 0xFF)<< 8) + (clipMax(((c1 )&0xFF) + ((((c2 )&0xFF)*a1)>>8), 0xFF) );}// color c1 -= c2 * alphainline UINT32 SubtractiveBlend32(unsigned long c1, unsigned long c2, unsigned long alpha) { INT32 a1 = (alpha)+1; return (clipMin(((c1>>16)&0xFF) - ((((c2>>16)&0xFF)*a1)>>8), 0)<<16) + (clipMin(((c1>> 8)&0xFF) - ((((c2>> 8)&0xFF)*a1)>>8), 0)<< 8) + (clipMin(((c1 )&0xFF) - ((((c2 )&0xFF)*a1)>>8), 0) );}
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement