Skip to content

How to create linear gradient?

VBrazhnik edited this page Jun 28, 2019 · 3 revisions

Here we will consider how to find the color between any two color points through linear interpolation.

First of all we need to find current point position between two points with known colors. Position value must be expressed in percentages.

The following function will help you find this value:

double percent(int start, int end, int current)
{
    double placement;
    double distance;

    placement = current - start;
    distance = end - start;
    return ((distance == 0) ? 1.0 : (placement / distance));
}

You can calculate this value depending on which delta value is bigger. Delta between x values of known points or delta between y values.

Part of code:

// ...
    double percentage;

    if (delta.x > delta.y)
        percentage = percent(start.x, end.x, current.x);
    else
        percentage = percent(start.y, end.y, current.y);
// ...

Then for creating each light (Red, Green, Blue) we need to get light from start and end point and use linear interpolation. At the end we need to get new color by union red, green and blue light.

Part of code:

// ...
    int red;
    int green;
    int blue;

    // Get percentage

    red = get_light((start.color >> 16) & 0xFF, (end.color >> 16) & 0xFF, percentage);
    green = get_light((start.color >> 8) & 0xFF, (end.color >> 8) & 0xFF, percentage);
    blue = get_light(start.color & 0xFF, end.color & 0xFF, percentage);
    return ((red << 16) | (green << 8) | blue);
int get_light(int start, int end, double percentage)
{
    return ((int)((1 - percentage) * start + percentage * end));
}

Complete code:

int get_light(int start, int end, double percentage)
{
    return ((int)((1 - percentage) * start + percentage * end));
}

int get_color(t_point current, t_point start, t_point end, t_point delta)
{
    int     red;
    int     green;
    int     blue;
    double  percentage;

    if (current.color == end.color)
        return (current.color);
    if (delta.x > delta.y)
        percentage = percent(start.x, end.x, current.x);
    else
        percentage = percent(start.y, end.y, current.y);
    red = get_light((start.color >> 16) & 0xFF, (end.color >> 16) & 0xFF, percentage);
    green = get_light((start.color >> 8) & 0xFF, (end.color >> 8) & 0xFF, percentage);
    blue = get_light(start.color & 0xFF, end.color & 0xFF, percentage);
    return ((red << 16) | (green << 8) | blue);
}

Basic information was found here.

Color for pixel

Everything is easy if you decided to use the following function:

int mlx_pixel_put(void *mlx_ptr, void *win_ptr, int x, int y, int color);

In this case, the order of lights is standard:

0 R G B
8 bits 8 bits 8 bits 8 bits

As you can see that the first byte is filled with zeros. It means that the alpha channel of color is not supported by minilibx.

You can find this information in mlx_pixel_put man file.

Also, this information is actual for color parameter in the function which displays text:

int mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string);

But if you decided to use an image, you will face with more complicated usage rules.

You will work with the following three functions:

void *mlx_new_image(void *mlx_ptr, int width, int height);
char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);
int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y);

And the most interesting is the second function with such parameters as bits_per_pixel and endian.

What is bits per pixel or bit-depth value?

The number of bits used to define a pixel's color shade is its bit-depth. True color is sometimes known as 24-bit color. Some new color display systems offer a 32-bit color mode. The extra byte, called the alpha channel, is used for control and special effects information.

For macOS value of bits_per_pixel is constant. You can find the following lines in source files of minilibx:

#define UNIQ_BPP 4
// assume here 32bpp little endian

char *mlx_get_data_addr(mlx_img_list_t *img_ptr, int *bits_per_pixel, int *size_line, int *endian)
{
    *bits_per_pixel = UNIQ_BPP * 8;
    *size_line = img_ptr->width * UNIQ_BPP;
    // ...
}

If you decided to support only macOS, you don't need to worry about the size of the variable with color. It is exactly as needed — 4 bytes (32 bits).

endian is the most important parameter that we have to consider.

For macOS its value is 0, which means little endian.

Information about endian value you can also find in source files of minilibx:

/*
** endian : 0 = sever X is little endian, 1 = big endian
** endian : useless on macos, client and graphical framework have the same endian
*/
// assume here 32bpp little endian

char *mlx_get_data_addr(mlx_img_list_t *img_ptr, int *bits_per_pixel, int *size_line, int *endian)
{
    // ...
    *endian = 0; // little endian for now on mac-intel
    // ...
}

Big-endian and little-endian are the formats of ordering bytes.

Big-endian is the format that we used to know as normal.

Little-endian order is reversed.

For color these two formats look like:

Byte number 0 1 2 3
Big endian 0 R G B
Little endian B G R 0

So in the case of little-endian format, you have to use reversed order of color components:

// ...
    int i;

    i = (x * fdf->bits_per_pixel / 8) + (y * fdf->size_line);
    fdf->data_addr[i] = color; // B — Blue
    fdf->data_addr[++i] = color >> 8; // G — Green
    fdf->data_addr[++i] = color >> 16; // R — Red
    fdf->data_addr[++i] = 0; // Alpha channel
// ...