Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a linear gradient in 2D array

I have a 2D bitmap-like array of let's say 500*500 values. I'm trying to create a linear gradient on the array, so the resulting bitmap would look something like this (in grayscale): Example gradient
(source: showandtell-graphics.com)

The input would be the array to fill, two points (like the starting and ending point for the Gradient tool in Photoshop/GIMP) and the range of values which would be used.

My current best result is this:

alt text http://img222.imageshack.us/img222/1733/gradientfe3.png

...which is nowhere near what I would like to achieve. It looks more like a radial gradient.

What is the simplest way to create such a gradient? I'm going to implement it in C++, but I would like some general algorithm.

like image 808
Matěj Zábský Avatar asked Feb 06 '09 18:02

Matěj Zábský


3 Answers

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.

like image 150
David Z Avatar answered Nov 13 '22 11:11

David Z


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:____.______________
             |
             |

like image 3
Dana the Sane Avatar answered Nov 13 '22 11:11

Dana the Sane


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).

like image 1
Richard Fabian Avatar answered Nov 13 '22 11:11

Richard Fabian