PDA

View Full Version : QPainter Antialiasing for QImage



mupilz
13th February 2012, 22:10
Hi,
first what I'm doing.
I'm coding a small GUI system for a game I'm writing. The existing GUI Toolkits usable on Ogre3D don't convince me enought that I use them. I'd like to use Qt, I even tried it but had some major issues doing so (major performance loss, Mouse/Keyboard events not working, etc.). So I decided to write my own toolkit. I already did write one, but it was very dirty and I didn't write it as an own library but coded it directly to the game. So I decided to re-write it.

Now my issue.
When rendering the Widgets, everything is fine, until I add a border radius. I guess, an image showing it is way easier than explaining:
click me (http://ompldr.org/vY3QyNQ)
I'm rendering directly on a QImage, using QPainter/QPainterPath.
The Pen is defined as follows:

painter.setPen(QPen(b, wB, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
The Brush is either a QColor or a QLinearGradient.
As render hint I only use Antialiasing.
The QImage is in the ARGB32 format.
I tried both approaches for the border radius, arcTo and cubicTo. Both ways have the same issue.

The problem occurs on different Graphics Systems.

here is the code I use for the rendering:

QImage AWidget::paint()
{
if (!visible())
{
cHasChange = false;
return QImage();
}
if (!hasChange())
{
cHasChange = false;
if (cLastImage != 0)
{
return *cLastImage;
}
}
cHasChange = false;

short w = width();
short h = height();
short radTL = 10;
short radTR = 10;
short radBR = 10;
short radBL = 10;

QBrush bg(QColor(100, 200, 100, 100));

QColor b(0, 0, 0, 255);

short wB = 1;


if (cHasStyle)
{
if (cBorder->radius()->isValid())
{
radTL = cBorder->radius()->tl();
radTR = cBorder->radius()->tr();
radBR = cBorder->radius()->br();
radBL = cBorder->radius()->bl();
}
if (cBackground->color()->isValid())
{
bg = QBrush(cBackground->color()->toQColor());
}
else if (cBackground->gradient()->isValid())
{
bg = QBrush(cBackground->gradient()->toQGradient());
}
if (cBorder->color()->isValid())
{
b = (cBorder->color()->toQColor());
}
if (cBorder->size()->isValid())
{
wB = cBorder->size()->left();
}
}

bool useCubic = false;

QPainterPath path;
path.moveTo(radTL, 0);
path.lineTo(w - radTR, 0);
if (radTR > 0)
{
if (useCubic)
{
path.cubicTo(w, 0, w, radTR, w, radTR);
}
else
{
path.arcTo(w - radTR * 2, 0, radTR * 2, radTR * 2, 90, -90);
}
}
path.lineTo(w, h - radBR);
if (radBR > 0)
{
if (useCubic)
{
path.cubicTo(w, h, w - radBR, h, w - radBR, h);
}
else
{
path.arcTo(w - radBR * 2, h - radBR * 2, radBR * 2, radBR * 2, 0, -90);
}
}
path.lineTo(radBL, h);
if (radBL > 0)
{
if (useCubic)
{
path.cubicTo(0, h, 0, h - radBL, 0, h - radBL);
}
else
{
path.arcTo(0, h - radBL * 2, radBL * 2, radBL * 2, -90, -90);
}
}
path.lineTo(0, radTL);
if (radTL > 0)
{
if (useCubic)
{
path.cubicTo(0, radTL, 0, 0, radTL, 0);
}
else
{
path.arcTo(0, 0, radTL * 2, radTL * 2, -180, -90);
}
}
path.closeSubpath();


QImage *img = new QImage(w, h, QImage::Format_ARGB32);
img->fill(0);
QPainter painter;
painter.begin(img);
painter.setRenderHints(QPainter::Antialiasing);
painter.setPen(QPen(b, wB, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter.setBrush(bg);

painter.drawPath(path);

paintEvent(&painter);

// +++ draw children
if (children().length() > 0)
{
for (int i = 0; i < children().length(); i++)
{
painter.drawImage(child(i)->x(), child(i)->y(), child(i)->paint());
}
}
// --- draw children

painter.end();

cLastImage = img;

return *img;
}

Furthermore would I like to ask, what the better way to render the border radius is - an arc or a bezier curve?

If some information is missing, feel free to tell.

cheers

mupilz
14th February 2012, 17:51
I fixed the problem.
I just had to seperate the rendering of the corners and the sides and set the pen width to half the width of the one for the sides.

Thread can be closed.