0

I'm trying to print a color gradient. When running, nothing is displayed, but when I select the entire window area with the mouse cursor, the colors appear but not a color gradient.

enter image description here

  • Function smooth takes the color and dimension information and starts the ncurses environment.

  • Colors are defined using the init_color function for and combined in pairs using init_pair to create custom color pairs.

  • Pixels are displayed using the mvaddch functions to place an empty character (' ') on the screen in the appropriate positions. Each column is divided into three equal parts, and each part is displayed with a different pair of colors.

The code:

#include <ncurses.h>

struct Color {
    int red, green, blue;
};

void smooth(int rows, int cols, struct Color leftColor, struct Color rightColor) {
    initscr();
    start_color();
    cbreak();
    noecho();
    curs_set(0);

    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            int r, g, b;
            r = leftColor.red + ((float)j / cols) * (rightColor.red - leftColor.red);
            g = leftColor.green + ((float)j / cols) * (rightColor.green - leftColor.green);
            b = leftColor.blue + ((float)j / cols) * (rightColor.blue - leftColor.blue);

            init_color(COLOR_BLACK, 0, 0, 0);
            init_color(COLOR_RED, r * 1000 / 255, 0, 0);
            init_color(COLOR_GREEN, 0, g * 1000 / 255, 0);
            init_color(COLOR_BLUE, 0, 0, b * 1000 / 255);
            init_pair(1, COLOR_RED, COLOR_BLACK);
            init_pair(2, COLOR_GREEN, COLOR_BLACK);
            init_pair(3, COLOR_BLUE, COLOR_BLACK);

            attron(COLOR_PAIR(1));
            mvaddch(i, j, ' ');
            attroff(COLOR_PAIR(1));

            attron(COLOR_PAIR(2));
            mvaddch(i, j + cols, ' ');
            attroff(COLOR_PAIR(2));

            attron(COLOR_PAIR(3));
            mvaddch(i, j + 2 * cols, ' ');
            attroff(COLOR_PAIR(3));
        }
    }

    refresh();
    getch();

    endwin();
}

int main() {
    int y, x;
    struct Color left, right;
    printf("Enter the RGB values for the left color: ");
    scanf("%d %d %d", &left.red, &left.green, &left.blue);
    printf("Enter the RGB values for the right color: ");
    scanf("%d %d %d", &right.red, &right.green, &right.blue);
    printf("Height: ");
    scanf("%d", &y);
    printf("Width: ");
    scanf("%d", &x);

    smooth(y, x, left, right);

    return 0;
}
Weather Vane
  • 33,872
  • 7
  • 36
  • 56
Cmistry
  • 173
  • 7
  • Without taking a close look at the code, definitely make sure your `$TERM` variable is set to something logical. Specifically one with 256 colors. – Jason Jul 10 '23 at 19:29
  • yes, it is. i did, `echo $TERM` result = `xterm-256color` – Cmistry Jul 10 '23 at 19:37
  • It looks as if `r = leftColor.red + ((float)j / cols) * (rightColor.red - leftColor.red);` is setting the colour without any need from any helpers (`g` and `b` too). – Weather Vane Jul 10 '23 at 19:47
  • 1
    If the colours of the spaces you output become visible when highlighted, then perhaps you have set the foreground colour, instead of the background colour. Select swaps the colours. Or try printing a proper character to see the foreground colour effect? – Gem Taylor Jul 10 '23 at 19:51

1 Answers1

3
  1. You inverted foreground and background. You use a black background and a variable foreground color (well, not so variable, see later). So, since you print a space, foreground color is useless. And only background is taken into account. Which is black. So you have a black screen

  2. Unless you select some part with the mouse. In which case, usual behaviour is to revert foreground and background. So there, you see what you expected to see.

  3. Once that corrected, you should get those 3 plain colors as result. Not what you expected. But that is for another reason: you can't recycle colors and pairs. They are supposed to be unique. And even if the terminal (which nowadays is the case of most terminal) has the ability to print a almost infinite number of colors, well, curses anyway needs that color index when you refresh (see for yourself: try to update colors in a loop, after the draw - and doing some refresh each times - you'll see that what you have already printed change colors. Color palette does not impact only future prints, but also past ones. So, you need a different color number and a different pair number for each color you use. Not just 1,2,3 recycled

  4. Also, don't redefine COLOR_BLACK, COLOR_RED, etc. They are already defined.

  5. And don't use the predefined color, unless you want to mess up your terminal (so, no color under 16)

All together,

#include <ncurses.h>
#include <unistd.h>

struct Color {
    int red, green, blue;
};

void smooth(int rows, int cols, struct Color leftColor, struct Color rightColor) {
    initscr();
    start_color();
    cbreak();
    noecho();
    curs_set(0);

    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            int r, g, b;
            r = leftColor.red + ((float)j / cols) * (rightColor.red - leftColor.red);
            g = leftColor.green + ((float)j / cols) * (rightColor.green - leftColor.green);
            b = leftColor.blue + ((float)j / cols) * (rightColor.blue - leftColor.blue);

            // I use j+16 color index, so a unique one, that won't be update afterward
            // I avoid using 16 first ones, because I like my default terminal colors
            init_color(j+16, r*1000/255, 0, 0);
            init_pair(j+16, COLOR_BLACK, j+16); // likewise for pairs`
            // Likewise for green
            init_color(j+16+cols, 0, g*1000/255, 0);
            init_pair(j+16+cols, COLOR_BLACK, j+16+cols);
            // Likewise for blue
            init_color(j+16+2*cols, 0, 0, b*1000/255);
            init_pair(j+16+2*cols, COLOR_BLACK, j+16+2*cols);
            // And likewise for all together, since I find quite strange your 3 red/green/blue 
            // gradient, and suspect that this 4th one is the one you wanted
            init_color(j+16+3*cols, r*1000/255, g*1000/255, b*1000/255);
            init_pair(j+16+3*cols, COLOR_BLACK, j+16+3*cols);

            attron(COLOR_PAIR(16+j));
            mvaddch(i, j, 'r'); // Just to be sure, and don't get fooled by space, I print something
            attroff(COLOR_PAIR(16+j));

            attron(COLOR_PAIR(j+16+cols));
            mvaddch(i, j + cols, 'g');
            attroff(COLOR_PAIR(j+16+cols));

            attron(COLOR_PAIR(j+16+2*cols));
            mvaddch(i, j + 2 * cols, 'b');
            attroff(COLOR_PAIR(j+16+2*cols));

            attron(COLOR_PAIR(j+16+3*cols));
            mvaddch(i, j + 3 * cols, 'X');
            attroff(COLOR_PAIR(j+16+3*cols));
        }
    }

    refresh();
    getch();
    // To make my point about "don't reuse color", here what happens we you do anyway
    for(int i=0; ;i++){
        for(int j=0; j<cols; j++){
            init_color((j+i)%cols+16, j*1000/cols, 0, 0);
            init_color((j+i)%cols+16+cols, 0, j*1000/cols, 0);
            init_color((j+i)%cols+16+2*cols, 0, 0, j*1000/cols);
            init_color((j+i)%cols+16+3*cols, j*1000/cols, j*1000/cols, j*1000/cols);
        }
        refresh();
        usleep(100000);
    }

    endwin();
}

int main() {
    int y, x;
    struct Color left, right;
    // Because I prefer minimal reproducible example, I hard code values (for SO only obviously)
    // People don't necessarily know what to fill these values (between 0 and 255? 0 and 1?) with
    // Besides, interactive program are wasting people time. Just print something, and explain
    // what you expected
    left.red=255; left.green=0; left.blue=0;
    right.red=0; right.green=255; right.blue=0;
    y=25;
    x=20;

    smooth(y, x, left, right);

    return 0;
}

See the animation at the end: I haven't reprinted anything. Just changed the color (not even the pairs). And int changes the whole terminal colors. So, this is what happens when you init_color(1,...): it updates everything on the screen that will and that has been printed with color 1.

chrslg
  • 9,023
  • 5
  • 17
  • 31
  • Very good. I changed `y = 5` and `x = 150`. The first gradient in the image is the result of your script. But I expected more like the gradient below as in the second gradient result of the [image](https://imgur.com/92FwMww). I'll study the code better and see what I can do – Cmistry Jul 10 '23 at 20:16
  • @Cmistry -- note that some terminals report xterm-256color and `can_change_color()` may even return true, yet the terminal does not _actually_ support changing colors. I suspect that this is the problem you face now. For example, Konsole supports 256 colors, but does not support changing colors showing results similar to what you report in the above comment. You may need to simply try another terminal emulator. [See this](https://stackoverflow.com/a/47691397/6879826) and [this](https://invisible-island.net/ncurses/ncurses.faq.html#xterm_generic). – ad absurdum Jul 10 '23 at 20:48