PDA

View Full Version : QPainter::drawLine(QPoint, QPoint) incorrect drawing



kamre
16th September 2013, 05:45
Why drawing lines with integer coords is not correct:

#include <QApplication>
#include <QLabel>
#include <QCheckBox>
#include <QPainter>
#include <QMouseEvent>
#include <QLayout>

class DrawLine: public QWidget {
Q_OBJECT
public:
DrawLine() : size(17), scale(40), radius(5), active(-1), hover(-1),
image(size, size, QImage::Format_RGB32), round(false),
antialiasing(false) {
int sz = size*scale;
setMinimumSize(sz, sz);
int half = sz/2;
pts[0] = QPoint(half/2, half);
pts[1] = QPoint(half*3/2, half);
renderLine();
setMouseTracking(true);
}
void emitCoords() {
QString str;
if (round) {
QPoint p0 = roundCoords(0);
QPoint p1 = roundCoords(1);
str.sprintf("P0: (%d, %d) P1: (%d, %d)", p0.x(), p0.y(), p1.x(), p1.y());
} else {
QPointF p0 = coords(0);
QPointF p1 = coords(1);
str.sprintf("P0: (%.2f, %.2f) P1: (%.2f, %.2f)", p0.x(), p0.y(), p1.x(), p1.y());
}
emit coordsUpdated(str);
}
public slots:
void setRound(bool round) {
this->round = round;
renderLine();
}
void setAntialiasing(bool antialiasing) {
this->antialiasing = antialiasing;
renderLine();
}
signals:
void coordsUpdated(QString coords);
protected:
virtual void paintEvent(QPaintEvent*) {
QPainter p(this);
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
QRgb cur = image.pixel(i, j);
p.fillRect(i*scale, j*scale, scale, scale, QColor(cur));
}
}
p.setPen(Qt::lightGray);
int sz = size*scale;
for (int i = 0; i < size; ++i) {
int x = i*scale;
p.drawLine(x, 0, x, sz);
p.drawLine(0, x, sz, x);
}
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::blue);
p.drawLine(pts[0], pts[1]);
p.setPen(Qt::NoPen);
for (int i = 0; i < 2; ++i) {
p.setBrush(i == hover ? Qt::yellow : Qt::blue);
p.drawEllipse(pts[i], radius, radius);
}
}
virtual void mousePressEvent(QMouseEvent *event) {
if (active == -1 && event->button() == Qt::LeftButton) {
int current = index(event->pos());
if (current != -1)
active = current;
}
}
virtual void mouseReleaseEvent(QMouseEvent *event) {
if (active != -1 && event->button() == Qt::LeftButton) {
active = -1;
}
}
virtual void mouseMoveEvent(QMouseEvent *event) {
if (active != -1) {
pts[active] = event->pos();
renderLine();
}
int current = index(event->pos());
if (current != hover) {
hover = current;
update();
}
}
private:
QPointF coords(int idx) {
return QPointF(pts[idx])/scale;
}
QPoint roundCoords(int idx) {
return (coords(idx) - QPointF(0.5, 0.5)).toPoint();
}
int index(QPoint p) {
for (int i = 0; i < 2; ++i) {
if (QLineF(p, pts[i]).length() <= radius)
return i;
}
return -1;
}
void renderLine() {
image.fill(qRgb(255, 255, 255));
QPainter p(&image);
p.setRenderHint(QPainter::Antialiasing, antialiasing);
p.setPen(Qt::red);
if (round) {
p.drawLine(roundCoords(0), roundCoords(1));
} else {
p.drawLine(coords(0), coords(1));
}
emitCoords();
update();
}
int size;
int scale;
int radius;
int active;
int hover;
QImage image;
QPoint pts[2];
bool round;
bool antialiasing;
};

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget widget;
QLabel label;
QCheckBox round("Round coords");
QCheckBox antialiasing("Antialiasing");
DrawLine draw;
QObject::connect(&draw, SIGNAL(coordsUpdated(QString)), &label, SLOT(setText(QString)));
QObject::connect(&round, SIGNAL(toggled(bool)), &draw, SLOT(setRound(bool)));
QObject::connect(&antialiasing, SIGNAL(toggled(bool)), &draw, SLOT(setAntialiasing(bool)));
QGridLayout layout;
layout.addWidget(&round, 0, 0);
layout.addWidget(&antialiasing, 0, 1);
layout.addWidget(&label, 0, 2);
layout.addWidget(&draw, 1, 0, 1, 3);
layout.setColumnStretch(2, 1);
widget.setLayout(&layout);
widget.show();
draw.emitCoords();
return app.exec();
}

#include "main.moc"



9596

kamre
17th September 2013, 07:48
Nobody uses QPainter::drawLine(QPoint, QPoint) or I am doing something wrong with this function?

high_flyer
17th September 2013, 08:46
What you are doing wrong is how you ask your question.
http://www.catb.org/esr/faqs/smart-questions.html#code

Specifically, it looks like the problem is in your position math - check it.

kamre
17th September 2013, 10:58
What you are doing wrong is how you ask your question.
Provided rather simple compilable example that reproduces the problem. What is wrong about it?


Specifically, it looks like the problem is in your position math - check it.
Ok, lets remove all user interaction and use hard coded values for the function:

#include <QApplication>
#include <QPainter>
#include <QWidget>

class DrawLine: public QWidget {
public:
DrawLine() : size(15), scale(40), image(size, size, QImage::Format_RGB32) {
int sz = size*scale;
setMinimumSize(sz, sz);
renderLine();
}
protected:
virtual void paintEvent(QPaintEvent*) {
QPainter p(this);
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
QRgb cur = image.pixel(i, j);
p.fillRect(i*scale, j*scale, scale, scale, QColor(cur));
}
}
p.setPen(Qt::lightGray);
int sz = size*scale;
for (int i = 0; i < size; ++i) {
int x = i*scale;
p.drawLine(x, 0, x, sz);
p.drawLine(0, x, sz, x);
}
}
private:
void renderLine() {
image.fill(qRgb(255, 255, 255));
QPainter p(&image);
p.setPen(Qt::red);
p.drawLine(QPoint(1, 4), QPoint(11, 2));
}
int size;
int scale;
QImage image;
};

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
DrawLine draw;
draw.show();
return app.exec();
}

Look at how y-coord is not respected.

high_flyer
17th September 2013, 11:52
Provided rather simple compilable example that reproduces the problem. What is wrong about it?
1. That you expect people to compile code for you.
Many people, me included, do this along side their work - I have not time to compile your code and debug for you.
I can help you narrow down the problem and offer potential problems - but you have to make it easy for me to focus on the problem - if you expect help.
2. You have provided no hint to help any potential helper to the area of the problem, rather expect people to read all the code check all of it and tell you where the problem is.
Nothing is wrong with doing that really, just don't act so surprised when you get no answers, that is all.

Again, looks like a math problem - my guess its the rounding.
What happens if you remove the rounding?

kamre
17th September 2013, 13:58
Nothing is wrong with doing that really, just don't act so surprised when you get no answers, that is all.
Ok, I see, thank you for your attention to my problem!


Again, looks like a math problem - my guess its the rounding.
What happens if you remove the rounding?
Look at the small piece of code posted above:


QPainter p(&image);
p.setPen(Qt::red);
p.drawLine(QPoint(1, 4), QPoint(11, 2));

No rounding at all, exact integer numbers are passed.

Paint event also doesn't use any floating point calculations:


virtual void paintEvent(QPaintEvent*) {
QPainter p(this);
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
QRgb cur = image.pixel(i, j);
p.fillRect(i*scale, j*scale, scale, scale, QColor(cur));
}
}
p.setPen(Qt::lightGray);
int sz = size*scale;
for (int i = 0; i < size; ++i) {
int x = i*scale;
p.drawLine(x, 0, x, sz);
p.drawLine(0, x, sz, x);
}
}

Note that size and scale are integer values.

Now lets look at the picture:
9600

The question is: why points (1, 4) and (11, 2) are not painted at all even if they were passed to the call of QPainter::drawLine?

wysota
17th September 2013, 15:22
Have you verified using the line drawing algorithm that they should be painted?

kamre
17th September 2013, 15:44
Have you verified using the line drawing algorithm that they should be painted?
Do you mean that drawing of line segment from pixel (x0, y0) to pixel (x1, y1) doesn't require filling these two pixels?

wysota
17th September 2013, 20:44
Do you mean that drawing of line segment from pixel (x0, y0) to pixel (x1, y1) doesn't require filling these two pixels?

I don't know, I haven't checked that using the line drawing algorithm. I'm asking whether you have done that since you claim something is wrong.

kamre
18th September 2013, 03:03
I don't know, I haven't checked that using the line drawing algorithm. I'm asking whether you have done that since you claim something is wrong.
I suppose you are joking, but lets look at the algorithms (http://en.wikipedia.org/wiki/Bresenham's_line_algorithm).

wysota
18th September 2013, 06:28
No, I'm not joking. You say something is wrong with the line so I'm asking whether this conclusion comes from your internal belief that the start and end points should be forced to be part of the line or whether you actually checked Qt's algorithm and found a flaw in it.

kamre
18th September 2013, 11:54
I'm asking whether this conclusion comes from your internal belief that the start and end points should be forced to be part of the line
This is the expected behavior of drawing line segment with pixel-based coords, is not it? It works so in other 2d drawing libraries like Cairo/Java2D.

But in Qt I have the following inconsistent behavior:

void renderLine() {
image.fill(qRgb(255, 255, 255));
QPainter p(&image);
// these line segments with red color fill start/end points
p.setPen(Qt::red);
p.drawLine(QPoint(7, 2), QPoint(7, 5));
p.drawLine(QPoint(9, 7), QPoint(12, 7));
p.drawLine(QPoint(7, 9), QPoint(7, 12));
p.drawLine(QPoint(2, 7), QPoint(5, 7));
p.drawLine(QPoint(5, 5), QPoint(3, 3));
p.drawLine(QPoint(9, 9), QPoint(11, 11));
// these line segments with blue color doesn't fill start/end points
// looks like y-coord is not respected
p.setPen(Qt::blue);
p.drawLine(QPoint(9, 5), QPoint(11, 3));
p.drawLine(QPoint(5, 9), QPoint(3, 11));
}

9602

whether you actually checked Qt's algorithm and found a flaw in it
Don't know about algorithm used in Qt and whether it has flaws, as user of the Qt I have just tried to draw something simple with QPainter::drawLine and it didn't work in expected and consistent way.

My question is: why QPainter::drawLine works so inconsistent and what can I do to get expected results from it?

wysota
18th September 2013, 12:42
Sorry, I don't understand what you mean. This is the result of your code on an image scaled up 10 times:

9603

What is inconsistent here?

The code used:


#include <QtGui>

void renderLine(QImage &image ) {
image.fill(qRgb(255, 255, 255));
QPainter p(&image);
// these line segments with red color fill start/end points
p.setPen(Qt::red);
p.drawLine(QPoint(7, 2), QPoint(7, 5));
p.drawLine(QPoint(9, 7), QPoint(12, 7));
p.drawLine(QPoint(7, 9), QPoint(7, 12));
p.drawLine(QPoint(2, 7), QPoint(5, 7));
p.drawLine(QPoint(5, 5), QPoint(3, 3));
p.drawLine(QPoint(9, 9), QPoint(11, 11));
// these line segments with blue color doesn't fill start/end points
// looks like y-coord is not respected
p.setPen(Qt::blue);
p.drawLine(QPoint(9, 5), QPoint(11, 3));
p.drawLine(QPoint(5, 9), QPoint(3, 11));
}


int main(int argc, char **argv) {
QApplication app(argc, argv);
QLabel l;
QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
renderLine(img);
img = img.scaled(160, 160);
l.setPixmap(QPixmap::fromImage(img));
l.show();
return app.exec();
}

high_flyer
18th September 2013, 12:54
I don't know what is different in the code you supplied here to the actual application you are coding.
I just ran your example code from the first post (because I could not see anything wrong with it and got curious) and it works perfectly, see attachment.
What ever it is which is going wrong its not in that code.
9604

EDIT: ah, wysota beat me to it :-)

kamre
18th September 2013, 13:07
What is inconsistent here?
This is perfectly fine result! And I also would like to obtain the same one.

But running your code (changed include to "QtWidgets") with my Qt 5.1.1 MinGW-OpenGL on Windows 7 (http://download.qt-project.org/official_releases/qt/5.1/5.1.1/qt-windows-opensource-5.1.1-mingw48_opengl-x86-offline.exe.mirrorlist) looks like this:
9605
Why do I have a different picture?

wysota
18th September 2013, 14:03
I have the same error you have when using Qt5.1 beta on Linux. I don't know whether this is caused by Qt5 or OpenGL. Using OpenGL would explain it but I can hardly believe QImage uses OpenGL to draw a line. You could file a bug report on this or compare line drawing algorithms in Qt4 and Qt5.