Important: Please read the Qt Code of Conduct -

Why creating a horizontal gradient of 2 colors contains black lines?

  • I want to create an image like this:

    that goes from one color on the one side, to another color to another side.

    So my code was
    @QImage image(1440, 900, QImage::Format_RGB32);
    QPainter p;

    QLinearGradient rect_gradient(0, 0, 1440, 0);
    rect_gradient.setColorAt(0, QColor(primary_col));
    rect_gradient.setColorAt(1, QColor(secondary_col));
    p.drawRect(0, 0, 1440, 900);
    p.drawImage(QRect(0,0,1440,900), image);
    QString filename = QFileDialog::getSaveFileName(this, tr("Save As"),home_path+"/pic_of_day.jpg",tr("Images (*.png *.xpm *.jpg)"));

    which works :). in this case 1st color is open blue and ends to black.
    "output image":

    Do you see the black lines in both pictures?

    "Now google horizontal gradient":

    Every image has black lines...

    Why? Is it possible to fix it? I tought of qpallete might fix something, but not sure on how to use it and then save it as an image.. even with qpallete, will it work?

  • Moderators

    These are not black lines. It's an artifact created due to not enough color depth to represent your gradient.

    You're using 32bit color (QImage::Format_RGB32) - 8 bits for red, green, blue and alpha. black/white/gray have all the components equal(except alpha) so you're getting maximum of 8 bits of difference. 8 bits means you can represent 256 color variations - 0 is black, 255 is white and anything in between is shade of gray.

    Now you're creating a gradient that has a 1440px width. Ideally each pixel in horizontal line should get a different color but you only have 256 of them, and that's assuming it's a full range 0-255, If you're going for dark gray to light gray or some color it might be 64 to 128 or something, so even few colors in between. This effect is called "color banding":

    So now some pixels will have to have the same color, and this creates thick stripes. When you look at it it looks like "black lines" you described or little inverted gradients, but it's just how a human eye works. If you magnify it in some image editor and check the colors you will see there are no actual black lines or gradients, just wide stripes of same color next to each other.

    Another thing is how well your monitor can actually represent neighboring color. On some monitors it might look like the darker ones are actually the same or slightly lighter and it will add to the striping effect.

    That's basically why large gradients of small variation should be avoided at all costs. There's just not enough color representation to make them look good.
    In theory for full black to full white gradient anything wider than 256px will look bad. In practice, on most consumer monitors, it's something like 220px.

    But sometimes you just need that large gradient, and there are techniques to fight banding.
    One way is to actually change the color a little bit, for example instead of (0,0,0) go for (0,4,0). It might not be noticeable but it will give you some extra color combinations and reduce banding. Of course this will vary from monitor to monitor.
    Another very common way is "dithering": It's basically adding some kind of pattern along the gradient fall to mask the color borders. If your pixels are small enough the effect is great. It' doesn't look that good when zooming in.

Log in to reply