3

Making a Blackberry app, want a Gradient class. What's the most effective way (as in, speed and battery life) to interpolate two colors? Please be specific.

// Java, of course
int c1 = 0xFFAA0055   // color 1, ARGB
int c2 = 0xFF00CCFF   // color 2, ARGB
float st = 0          // the current step in the interpolation, between 0 and 1

Help from here on. Should I separate each channel of each color, convert them to decimal and interpolate? Is there a simpler way?

interpolatedChannel = red1+((red2-red1)*st)
interpolatedChannel = interpolatedChannel.toString(16)

^ Is this the right thing to do? If speed and effectiveness is important in a mobile app, should I use bitwise operations?

Help me!

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
navand
  • 1,379
  • 1
  • 16
  • 20

4 Answers4

10

You'll have to separate channels, but there's no need to convert them to decimal.

For example, if you allow 256 possible gradients:

red = red1 + ((red2 - red1) * stage / 256)

EDIT: Since you said you don't know much about bit management, here's a quick way to split channels:

red = color & 0x000000ff;
green = color & 0x0000ff00;
blue = color & 0x00ff0000;
alpha = color >> 24;

And combining them back:

color = (alpha << 24) | blue | green | red;

From here, the details should normally be handled by the compiler optimizations. If anything, you're looking for the best algorithm.

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Raymond Martineau
  • 5,959
  • 1
  • 20
  • 12
  • So how do I separate the channels? Back when I did actionscript, once I needed to do it. I turned the hex value into a string, separated the string into 2 character components, and turned each component back into an integer. I'm sure this is a lousy way of doing it, so please help me here. Also, I need to know if that formula is the best idea or if bitwise ops are faster/better. – navand Apr 13 '10 at 15:51
  • You shouldn't need to convert hex into a string to separate it. The only recommendation is to take a look at bitwise operators for the language, which are usually "&", "|", "^", "<<" and ">>". – Raymond Martineau Apr 13 '10 at 15:56
  • 1
    you do want to mask the interpolated channels before combining back. the division used to compute the green channel would pollute the red channel with the green remainder (e.g.) otherwise. – Bahbar Apr 13 '10 at 16:19
  • thanks for the answer. Now I have the separate channels, but I still need the interpolation thing. Help? – navand Apr 13 '10 at 19:29
  • I did... I just thought there was a way of doing that formula with bitwise operations, and that doing so would be faster. Am I right? – navand Apr 14 '10 at 16:17
  • Wait a second... are you confusing the red and blue channels? – navand Apr 15 '10 at 16:05
  • @navand: no you're not right. interpolation is inherently an arithmetic operation. What makes you think bit-wise operation would be faster than arithmetic operations that are natively supported by most processors ? do you really want to implement a multiplier ? – Bahbar Apr 16 '10 at 21:17
7
private function interpolateColorsCompact( a:int, b:int, lerp:Number ):int
{ 
   var MASK1:int = 0xff00ff; 
   var MASK2:int = 0x00ff00; 

   var f2:int = 256 * lerp;
   var f1:int = 256 - f2;

   return   ((((( a & MASK1 ) * f1 ) + ( ( b & MASK1 ) * f2 )) >> 8 ) & MASK1 ) 
          | ((((( a & MASK2 ) * f1 ) + ( ( b & MASK2 ) * f2 )) >> 8 ) & MASK2 );

} 

Not sure if this is the most compact way of doing it, but it uses less local variables and less operators than the classic method that splis them into 3 channels first.

Oh - and sorry that this is Actionscript, but it should be clear how to convert this to Java.

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Quasimondo
  • 2,485
  • 1
  • 22
  • 30
  • 1
    I don't know how this bitmagic works, but if you change mask2 to 0xFF00FF00 (leave everything else unchanged) it will linearly interpolate the alpha between a and b as well (assuming ARGB) – Dr. Andrew Burnett-Thompson Jun 01 '14 at 19:22
2

Updated my answer (found a better way):

The following technique will lose 1 bit precision per channel, but it's extremely fast, since you won't have to split the colors into channels:

int color1 = ...;
int color2 = ...;
int interpolatedColor = ((color1 & 0xFEFEFEFE) >> 1) + 
                        ((color2 & 0xFEFEFEFE) >> 1));

So, first you AND both colors by 0xFEFEFEFE. This removes the last bit per channel (reduces precision, as I said). After that, you can safely divide the entire value by 2 (implemented as a right-shift by 1). Finally, you just add up the two values.

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Chris Lercher
  • 37,264
  • 20
  • 99
  • 131
0

Just the java version of /u/Quasimondo's answer:

public static int mixColors(int a, int b, float ratio){
    int mask1 = 0x00ff00ff;
    int mask2 = 0xff00ff00;

    int f2 = (int)(256 * ratio);
    int f1 = 256 - f2;

    return (((((a & mask1) * f1) + ((b & mask1) * f2)) >> 8) & mask1) 
         | (((((a & mask2) * f1) + ((b & mask2) * f2)) >> 8) & mask2);
}

If you only need exact 50/50 ratios you can cut out the bitshifting:

public static int mixColors(int a, int b){
    int mask1 = 0x00ff00ff;
    int mask2 = 0xff00ff00;

    return (((a & mask1) + (b & mask1)) & mask1) 
         | (((a & mask2) + (b & mask2)) & mask2);
}