While looking for ways to improve the KDE color selection dialog, I stumbled upon the slow rendering of the hue/saturation color gradient. Using a simple trick such gradients can be rendered using hardware facilities.
The goal is to render a two-dimensional gradient as depicted below. The current implementation allocates a properly sized QImage, and computes the RGB color values for each individual pixel.
Hue/saturation two-dimensional gradient
This requires, however, nearly hundred thousand conversions from HSV to RGB color values, and is the primary cause for the slowness of the dialog.
When looking at the resulting RGB values, you will notice that each row (“scanline”) is actually a one-dimensional linear gradient, so it could be rendered using QLinearGradient.
Looking further, you will also see that there is a linear gradient in each column. Such a gradient is called “bilinear”, but Qt does not offer a two-dimensional QGradient class.
The trick is to create this bilinear gradient by scaling up a very small QImage. The image you see above is actually only 7×2 pixels large. The image is then rendered to a larger QPixmap with scaling applied, so that possible hardware scaling can be used.
A requirement is that pixmap scaling supports bilinear interpolation. This mode, however, is even available with the software scaling algorithms built into Qt.
Clever!
This may be obvious to you but why it is not possible to use a simple png to display this gradient ?
@Heller, the gradient changes depending on the value in the slider next to it, so you would have to store 256 images (actually 1536, because there are 6 “modes” the gradient can be in).
Why is the image 7×2 and not 4×2 (red,green,blue,red)?
@Johannes, if you do a linear blend between red and green, you do not get a full yellow, but something darker:
http://www.herethere.net/~samson/php/color_gradient/?cbegin=FF0000&cend=00FF00&steps=4
For those of us slowpokes 🙂 d’you mind sharing a piece of code?
@kdepepo: Your right! Sorry I sometimes write before I think. 🙂
@Alex, the code is in KDE svn 🙂 It basically goes like this (not tested):
QImage image(2, 2); // 4-point gradient
image.setPixel(0, 0, QColor(Qt::black).rgb());
image.setPixel(0, 1, QColor(Qt::green).rgb());
image.setPixel(1, 0, QColor(Qt::red).rgb());
image.setPixel(1, 1, QColor(Qt::yellow).rgb());
QRect destRect(0, 0, 200, 200);
painter.setRenderHint(QPainter::SmoothPixmapTransforms, true);
painter.drawImage(destRect, image, QRectF(0.5, 0.5, 1, 1));