SDL2 - Color Gradient


#1

Hello,

I’ve been attempting to create a function that takes in a specification for a square, where to place it on screen, width, height etc etc, then starting with a starting color transitions over to another set one. So a color gradient.

Now I could do this in Phtoshop sure, but I’d prefer to be able to do it in a function. I’ve done some Googling on the topic, and did find some examples though they were all done in SDL1.2, and were a fixed dimension and position on screen, so they don’t suit what I’m after.

So as an example for the start of the function:

void drawGradientBox(int x, int y, int width, int height, int startR, int startG, int startB, int endR, int endG, int endB)
{
SDL_Rect box = {x,y,width,height};
SDL_SetRenderDrawColor(sdlRenderer, r, g, b, alpha);
SDL_RenderFillRect(sdlRenderer, &box);
}

Then I’d imagine you’d have to go through a for loop going from the starting color and going upward etc etc. Though in my test attempts, nothings worked.

Looking for some assistance on the issue. Anyone able to provide a code example for SDL2?

Thanks


#2

There is nothing here that allows you to draw a “gradient”, you should have at least a for loop. But some problem arises.

One is your definition of gradient and how do you move from a color to another, the first hint is stop to think in colors and think in numbers.

For example to move from Pure Red (255,0,0) to black (0,0,0) you need at least a for loop iterating over the first integer.

But when you say gradient from (250,210, 25) (I don’t even know what color is this) to for example (10,30,50) (I also don’t even know what color is this.)

You need to iterate at least using a interpolation from the first red (250) to the last red (10), the same with the other 3 colors.

The next question is: How many steps? 2, 20, 100? That will guide your iterating step. Lets say for sake of simplicity that you want 50 steps

a step is taken from formula

(C_1 - C_0 ) / steps

In this case (the red case) we have:

(250-10)/50 == 240/50 == 4.8

That means you need to increase 4.8 for every iteration step.

The last question is, how do you will draw this.

Horizontal? Vertical? Radial? Diagonal? From some coordinate to some coordinate?

If we suppose the simplest case horizontal one. You have to use the same step above define (50) and draw squares one over another, each one horizontaly smaller than the previous one, giving you the visual illusion of gradient. Of course you can split the height by the number of steps and moving down a small rectangle.

And many other issues can arise as the color that will show in transitions, how do we perceive color and color blendings.

This isn’t a so trivial matter. As you could notice.


#3

Quite the challenge definitely.

So what I’d like is for the size of the area to be set in the function itself, and so not hard coded. All of the examples for color gradients that I’ve found online all used fixed hard coded sizes.

So the function would start out something like:

void drawGradientBox(int x, int y, int width, int height, color1, color2)

Then have it draw the box at the position x/y of the width and height set, use color1 as the start color and transition to color2.


#4

Understood, but lets say, it’s an horizontal or vertical gradient? I will code some here for a horizontal gradient and show you.

Feel free to reach me at morgado (this is a regular dot) igor (at@) gma(i1) punto C… you know how it finish. Just to avoid the bots.


#5

Tried to make a bit verbose, and with command line parameter (and a small help text). Hope it helps.

#include <SDL2/SDL.h>
#include <math.h>
#include <stdio.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

void drawHorizontalGradientBox(SDL_Renderer * renderer,
        const int x, const int y, const int w, const int h, const float steps,
        const SDL_Color c1, const SDL_Color c2, const int fill)
{

    /* Acumulator initial position */
    float yt = y;
    float rt = c1.r;
    float gt = c1.g;
    float bt = c1.b;
    float at = c1.a;
    
    /* Changes in each attribute */
    float ys = h/steps;
    float rs = (c2.r - c1.r)/steps;
    float gs = (c2.g - c1.g)/steps;
    float bs = (c2.b - c1.b)/steps;
    float as = (c2.a - c1.a)/steps;

    for(int i = 0; i < steps ; i++)
    {
        /* Create an horizontal rectangle sliced by the number of steps */
        SDL_Rect rect = { x, yt, w, ys+1 };

        /* Sets the rectangle color based on iteration */
        SDL_SetRenderDrawColor(renderer, rt, gt, bt, at);

        /* Paint it or coverit*/
        if(fill)
            SDL_RenderFillRect(renderer, &rect);
        else
            SDL_RenderDrawRect(renderer, &rect);

        /* Update colors and positions */
        yt += ys;
        rt += rs;
        gt += gs;
        bt += bs;
        at += as;
    }
}

int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window * window = SDL_CreateWindow("Gradients", 
            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 
            SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);

    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    SDL_Event event;

    int x = SCREEN_WIDTH / 4;
    int y = SCREEN_HEIGHT / 4;
    int w = SCREEN_WIDTH / 2;
    int h = SCREEN_HEIGHT / 2;
    int s = 10;
    SDL_Color c1 = (SDL_Color) { 0, 0, 0, 255 };
    SDL_Color c2 = (SDL_Color) { 255, 255, 255, 255 };
    int fill = 0;

    if(argc > 1 && argc != 15)
    {
        printf("%s x y w h s r1 g1 b1 a1 r2 g2 b2 a2 fill  (all integers)\n", argv[0]);
        printf("Examples:\n\n");
        printf("./horizontal_gradient  120 120 200 200 50  255 200 200 255 255 10 10 255 1\n");
        printf("./horizontal_gradient  120 120 200 200 20  0 255 200 255 255 10 10 255 0\n");
        printf("\n");
        exit(1);
    }
    else if(argc > 1 && argc == 15)
    {
        x = atoi(argv[1]);
        y = atoi(argv[2]);
        w = atoi(argv[3]);
        h = atoi(argv[4]);
        s = atoi(argv[5]);
        c1 = (SDL_Color) {
            atoi(argv[6]),
            atoi(argv[7]),
            atoi(argv[8]),
            atoi(argv[9])
        };
        c2 = (SDL_Color) {
            atoi(argv[10]),
            atoi(argv[11]),
            atoi(argv[12]),
            atoi(argv[13])
        };
        fill = atoi(argv[14]);
    }

    int quit = 0;
    SDL_Rect contour = { x-1, y-1, w+2, h+2};

    while(!quit)
    {
        while(SDL_PollEvent(&event) != 0)
            if(event.type == SDL_QUIT)
                quit = 1;

        // Draw a ugly background
        SDL_SetRenderDrawColor(renderer, 100, 100, 100, 10);
        SDL_RenderClear(renderer);

        drawHorizontalGradientBox(renderer, x, y, w, h, s, c1, c2, fill);

        // Draw a contour for sake of debug...
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 50); 
        SDL_RenderDrawRect(renderer, &contour);

        SDL_RenderPresent(renderer);
        SDL_Delay(10);

    }
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

#6

Hi, there is a quick way to do a linear gradient in SDL:

  • Just create a texture with 2 pixels: color1 and color2. (so the size of the texture is 2x1)
  • Set the render_scale_quality hint to 1 or 2:
    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, “1”);
  • Then scale the texture to the desired size with SDL_RenderCopy

You can even have diagonal gradient with this technique: you just need to use SDL_RenderCopyEx with an angle to rotate the texture, and clip it again to a rectangle.

This method allows to do multi-color gradients: for instance, start with a 3-pixel texture.

The disadvantage of this method is that you are not 100% sure that all renderers will accept the RENDER_SCALE_QUALITY. For me, it works nicely.


#7

Nice to know! Thank you for the hint.