Some time ago we added support for pixmap effects to Qt, aka blurring, sharpening, etc. You may have noticed the example in examples/painting/pixmapfilter. They have implementation based on the underlying graphics system so when you’re filtering to a GL based QPainter you’re likely to get GL acceleration. We only added a few effects, so I thought I would show you how to write your own…
So I’ll show you the bloom filter, or glow or gloom all depending on where you read about it. The concept is to make the light parts of an image and make them glow, like illustrated below:
![]() Original |
![]() with Gloom effect |
There are a number of ways to achieve this, so I just picked on that seemed to produce ok results while still being slightly “tutorial” like. The way its implemented is that I first mask away all the pixels that are darker than a given threshold. Then I scale this masked image down, blur it, scale it up again and draw it using QPainter::CompositionMode_Plus. The scaling down is just to get an extra blurring effect and the composition mode is to add to the brightness rather than doing normal alpha blending.
So in a bit more detail… I implemented the brightness thresholding as one separate pixmap filter, called QPixmapBrightnessFilter. To do the actual processing I override the pure virtual draw() function to do the actual processing onto the device.
virtual void draw(QPainter *painter,
const QPointF &p,
const QPixmap &src,
const QRectF &srcRect = QRectF()) const
{
QImage image = src.toImage();
if (image.format() != QImage::Format_ARGB32_Premultiplied)
image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
QRect rect = srcRect.toRect();
if (rect.isEmpty())
rect = src.rect();
int ymin = rect.y();
int ymax = rect.y() + rect.height();
int xmin = rect.x();
int xmax = rect.x() + rect.width();
for (int y=ymin; y<ymax ; ++y) {
uint *pixels = (uint *) image.scanLine(y);
for (int x=xmin; x<xmax; ++x) {
uint pix = pixels[x];
int brightness = qRed(pix) + qGreen(pix) + qBlue(pix);
if (brightness < 400)
pixels[x] = 0;
}
}
painter->drawImage(p, image, rect);
}
As you can see, I simply go through the pixels and set the pixel to fully transparent if the sum of the color channels is less than 400. I played a bit around with different values, and for the images I tested on 400 seemed like an “ok” number… feel free to experiment or make it settable
The bloom filter itself is implemented via the brightness filter in combination with QPainter functions and the QPixmapConvolutionFilter.
virtual void draw(QPainter *painter,
const QPointF &p,
const QPixmap &src,
const QRectF &srcRect = QRectF()) const
{
// Draw the original...
painter->drawPixmap(p, src, srcRect);
QPixmap source = src;
if (!srcRect.isEmpty())
source = source.copy(srcRect.toRect() & src.rect());
QPixmapBrightnessThresholdFilter threshold;
QPixmap masked(source.size());
masked.fill(Qt::transparent);
QPainter pt(&masked);
threshold.draw(&pt, QPointF(0, 0), source);
pt.end();
source = masked;
painter->save();
painter->setCompositionMode(QPainter::CompositionMode_Plus);
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->setOpacity(painter->opacity() * 0.5);
QPixmapConvolutionFilter filter;
qreal kernel[] = {
1/16., 2/16.0, 1/16.0,
2/16., 4/16.0, 2/16.0,
1/16., 2/16.0, 1/16.0
};
filter.setConvolutionKernel(kernel, 3, 3);
for (int i=0; i<4; ++i) {
painter->scale(2, 2);
source = source.scaled(source.width() / 2.0,
source.height() / 2.0);
filter.draw(painter, p, source);
}
painter->restore();
}
At the top of the function I draw the original pixmap. Then I use the QPixmapBrightnessFilter to extract the light-intensive parts of the and store the results in the masked pixmap. Then I save the painter play nice. As I mentioned before, I want to add brightness to the image, so I switch the composition mode to QPainter::CompositionMode_Plus which basically adds more light to the image. Since I already drew the original pixmap, the result will now only be brighter. Because the effect was rather intense, I toned it a bit down using QPainter::setOpacity(0.5).
The image is then scaled down by to half size and drawn blurred at double size. I repeat this progressively four times, thus drawing the original image 1/2, 1/4, 1/8 and 1/16 size.
Since most of the implementation in the QPixmapBloomFilter::draw() function is based on QPainter calls and the exisint QPixmapConvolution, these things can be hardware accelerated, so if I run the example with -graphicssystem opengl or the painter is initialized on a QGLWidget, these things will be hardware accelerated. The only software parts is then the thresholding and downscaling of the pixmaps. Unfortunately, there was a bug in the GL based pixmap filters, so -graphicssystem opengl crashes, but I submit a patch today so it should work in snapshots starting from Monday
So this implementation is only partially H/W accelerated, but it illustrates how filters work. Maybe I get around to do this “properly” over Christmas.
The source code should end up here once the server is synced:
svn checkout svn://labs.trolltech.com/svn/graphics/dojo/bloomfilter
Have a nice holiday!



