views:

1139

answers:

4
+8  A: 

This is really a math question, so it might be debatable whether it really "belongs" on Stack Overflow, but anyway: you need to project the coordinates of each point in the image onto the axis of your gradient and use that coordinate to determine the color.

Mathematically, what I mean is:

  1. Say your starting point is (x1, y1) and your ending point is (x2, y2)
  2. Compute A = (x2 - x1) and B = (y2 - y1)
  3. Calculate C1 = A * x1 + B * y1 for the starting point and C2 = A * x2 + B * y2 for the ending point (C2 should be larger than C1)
  4. For each point in the image, calculate C = A * x + B * y
  5. If C <= C1, use the starting color; if C >= C2, use the ending color; otherwise, use a weighted average:

    (start_color * (C2 - C) + end_color * (C - C1))/(C2 - C1)

I did some quick tests to check that this basically worked.

David Zaslavsky
+1, what is programming if not applied math? =)
Crashworks
Maybe I am stupid, but I couldn't make it work. It worked for horizontal and vertical gradients, but no luck for angled ones. +1 anyways.
CommanderZ
Just double-checked and in Mathematica it worked perfectly for all angles I tested, so I'm fairly positive the formula itself is valid. What trouble did you have?
David Zaslavsky
+2  A: 

In your example image, it looks like you have a radial gradient. Here's my impromtu math explanation for the steps you'll need. Sorry for the math, the other answers are better in terms of implementation.

  1. Define a linear function (like y = x + 1) with the domain (i.e. x) being from the colour you want to start with to the colour your want to end with. You can think of this in terms of a range the within Ox0 to OxFFFFFF (for 24 bit colour). If you want to handle things like brightness, you'll have to do some tricks with the range (i.e. the y value).
  2. Next you need to map a vector across the matrix you have, as this defines the direction that the colours will change in. Also, the colour values defined by your linear function will be assigned at each point along the vector. The start and end point of the vector also define the min and max of the domain in 1. You can think of the vector as one line of your gradient.
  3. For each cell in the matrix, colours can be assigned a value from the vector where a perpendicular line from the cell intersects the vector. See the diagram below where c is the position of the cell and . is the the point of intersection. If you pretend that the colour at . is Red, then that's what you'll assign to the cell.
             |
             c
             |
             |
    Vect:____.______________
             |
             |

Dana the Sane
I was thinking about something along these lines, but I hoped I could find some simplier solution. It took me a hour and half to derive the correct formula to calculate the correct point on the line A->B :)
CommanderZ
Good stuff, if you have a chance, post it in your question. I'm sure someone will find it useful.
Dana the Sane
+1  A: 

I'll just post my solution.

int ColourAt( int x, int y )
{
  float imageX = (float)x / (float)BUFFER_WIDTH;
  float imageY = (float)y / (float)BUFFER_WIDTH;

  float xS = xStart / (float)BUFFER_WIDTH;
  float yS = yStart / (float)BUFFER_WIDTH;
  float xE = xEnd / (float)BUFFER_WIDTH;
  float yE = yEnd / (float)BUFFER_WIDTH;
  float xD = xE - xS;
  float yD = yE - yS;

  float mod = 1.0f / ( xD * xD + yD * yD );

  float gradPos = ( ( imageX - xS ) * xD + ( imageY - yS ) * yD ) * mod;

  float mag = gradPos > 0 ? gradPos < 1.0f ? gradPos : 1.0f : 0.0f;

  int colour = (int)( 255 * mag );
  colour |= ( colour << 16 ) + ( colour << 8 );
  return colour;
}

For speed ups, cache the derived "direction" values (hint: premultiply by the mag).

Richard Fabian
A: 

There are two parts to this problem.

  1. Given two colors A and B and some percentage p, determine what color lies p 'percent of the way' from A to B.

  2. Given a point on a plane, find the orthogonal projection of that point onto a given line.

The given line in part 2 is your gradient line. Given any point P, project it onto the gradient line. Let's say its projection is R. Then figure out how far R is from the starting point of your gradient segment, as a percentage of the length of the gradient segment. Use this percentage in your function from part 1 above. That's the color P should be.

Note that, contrary to what other people have said, you can't just view your colors as regular numbers in your function from part 1. That will almost certainly not do what you want. What you do depends on the color space you are using. If you want an RGB gradient, then you have to look at the red, green, and blue color components separately.

For example, if you want a color "halfway between" pure red and blue, then in hex notation you are dealing with

ff 00 00

and

00 00 ff

Probably the color you want is something like

80 00 80

which is a nice purple color. You have to average out each color component separately. If you try to just average the hex numbers 0xff0000 and 0x0000ff directly, you get 0x7F807F, which is a medium gray. I'm guessing this explains at least part of the problem with your picture above.

Alternatively if you are in the HSV color space, you may want to adjust the hue component only, and leave the others as they are.

anonnn
I am working with grayscale only, which significantly simplifies color blending.
CommanderZ