PDA

View Full Version : Rare error with line style



avis_phoenix
22nd November 2007, 05:09
I write a program that draw lines, this program draws the entire line, the program works fine until you change the style of drawing the line, for example if the style is dotted; the program becomes increasingly unstable in this vertical line, what can I do?

wysota
22nd November 2007, 10:08
Could we see some code?

avis_phoenix
24th November 2007, 18:53
I upload my all project, in eobjects.cpp in EObjects::paint put the instruccions for draw the line, the case 'b' (is lines) in subcase 'A' (this is the case with problem) in refresh calculate the points P1, P2 in case 'b' and subcase 'A' (in the second switch) if the source is very complicated then i will write a small project, sorry

jpn
25th November 2007, 14:14
I upload my all project, in eobjects.cpp in EObjects::paint put the instruccions for draw the line, the case 'b' (is lines) in subcase 'A' (this is the case with problem) in refresh calculate the points P1, P2 in case 'b' and subcase 'A' (in the second switch) if the source is very complicated then i will write a small project, sorry
The UI is in a foreign language. So... what should one do to reproduce the problem? I vote for a small test app. :)

avis_phoenix
28th November 2007, 04:46
Sorry ...
Here I put the small project without foreign languages (apologies), execute this program and move the points, after with the combination Ctrl + C changes the style of the line and see the error... Thanks

Uwe
28th November 2007, 08:40
I remember several bugs and performance issues with drawing lines in the history of Qt4. AFAIR they all were bugs/problems of the homebrewed raster paint engine. The raster paint engine is involved, when painting on Windows or when painting to a QImage on all platforms.

Without looking at your code it is important to know which OS and which Qt version you are using.

Uwe

avis_phoenix
29th November 2007, 06:10
Well, I use X11 (Ubuntu) and Windows, with Qt 4.3.2 (but this problem since I use Qgraphics items, in Qt 4.2... (I hope it was my mistake and it is easy to correct)

Uwe
29th November 2007, 08:25
Hm, after rotating the line some time the application gets slower and slower until it hangs ( Linux/X11 ) for some time. Looking (randomly) at the stack the application is busy inside QPainterPath::toFillPolygon, where it iterates over a polygon of 32513(!) pieces.
So this should be TrollTechs business und I recommend to file a bug report.

In the meantime:

a) Calculate the visible part of your line before(!) painting and do your QPainter::drawLine for this part only. The 32513 pieces indicate, that Arthur needs most of the time rendering stuff, that is not visible at all.
b) disable antialiasing, when you have a non solid line

HTH,
Uwe

PS: Try the code from clipLine in qpaintengine_x11.cpp

wysota
29th November 2007, 10:13
Comment out "setCacheMode ( QGraphicsView::CacheBackground );" from the view class. You don't have a background so there is no point caching anything. When you rem it out, the app works just fine.

BTW. Implementing shape() would also be advised.

avis_phoenix
1st December 2007, 00:31
Well, how should I write the bug report? Since i have no idea about how to write one.

wysota
1st December 2007, 00:48
But what do you want to report?

avis_phoenix
2nd December 2007, 05:29
But even comment "setCacheMode (QGraphicsView: CacheBackground);" anyway freezes

wysota
2nd December 2007, 10:12
You mean that if you take the code from this post
http://www.qtcentre.org/forum/p-rare-error-with-line-style-post55429/postcount5.html
and comment out background caching, the code is still slow?

avis_phoenix
2nd December 2007, 18:49
yes (when the line is vertical and don't solid style)

wysota
2nd December 2007, 19:52
Hmm... could someone please verify this? The code works blindingly fast for me...

Edit: Indeed the code slows down if the line approaches a vertical direction. But this seems to be a problem related to the math used to calculate the line, not to redrawing the line inside graphics view.

Edit:
Seems you are trying to draw a line between such coordinates when it is vertical:
QPointF( -512 , -133155 ) QPointF( 512 , 110557 )

I'm not surprised the line is drawn so slow... Unfortunately improving the algorithm in Qt is very hard to achieve when the line is not solid, because the exact dash positions have to be calculated based on real (not culled) coordinates. Changing the equation here would help a lot.

Edit:
Your code is faulty... Never write such code:

if(p1.x()-p2.x()!=0) if x() returns floating point values. Instead use a neighbourhood, like so:

if(qAbs(p1.x()-p2.x())>=0.1)
The next thing to do is to provide a good implementation of shape() for the line, so that it doesn't have to be constantly redrawn when you move your mouse over the canvas.

wysota
2nd December 2007, 20:42
I implemented an example app, that does more or less the same as yours. Notice, that it doesn't slow down the application when the line is vertical.

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>


class DotItem : public QGraphicsEllipseItem {
public:
DotItem() : QGraphicsEllipseItem(){
setRect(-5,-5,10,10);
setFlag(ItemIsMovable);
QPen pen;
pen.setWidth(2);
pen.setColor(Qt::red);
setPen(pen);
m_line = 0;
}
void setLine(QGraphicsLineItem *l){ m_line = l; }
protected:
QVariant itemChange ( GraphicsItemChange change, const QVariant & value ){
if(m_line){
QLineF l = m_line->line();
QPointF oldPos = pos();
QPointF p1 = l.p1();
QPointF p2 = l.p2();
if(oldPos==p1){
p1 = value.toPointF();
} else if(oldPos==p2){
p2 = value.toPointF();
} else return value;
m_line->setLine(QLineF(p1,p2));
}
return QGraphicsEllipseItem::itemChange(change, value);
}
private:
QGraphicsLineItem *m_line;
};

int main(int argc, char **argv){
QApplication app(argc, argv);
QGraphicsView view;
view.setScene(new QGraphicsScene(QRectF(-512, -512, 1024, 1024), &view));
view.setRenderHints(QPainter::Antialiasing);
DotItem *i1 = new DotItem;
DotItem *i2 = new DotItem;
QGraphicsLineItem *line = new QGraphicsLineItem;
QPointF p1(-212, 212);
QPointF p2(212, -212);
line->setLine(QLineF(p1, p2));
QPen pen;
pen.setStyle(Qt::DashDotLine);
pen.setWidth(2);
line->setPen(pen);
view.scene()->addItem(line);
i1->setPos(p1); i2->setPos(p2);
view.scene()->addItem(i1); view.scene()->addItem(i2);
i1->setLine(line); i2->setLine(line);
view.show();
return app.exec();
}

Uwe
3rd December 2007, 10:07
But what do you want to report?
When using a non solid style Qt seems to split the line into pieces and to render them one by one. In the demo these were ~32000 pieces, what are too much (for fast rotations) to be antialiased in time on my low budget notebook ( the fan gets noisy and the application hangs ).

But considering that you can see < 100 dashes/dots on the viewport ( only a small part of the line is visible), you will agree, that Arthur could do the job much better by clipping the line before rendering.

I remember, that I was talking to the Trolls about introducing polygon clipping as an optional render hint before Qt4.0 was out: the main problem are the overruns, when the coordinates are mapped to X11 (= shorts). The Qt4 source includes the clipping algos, but obviously there is no API to make them available to the user.

Uwe

wysota
3rd December 2007, 10:25
But considering that you can see < 100 dashes/dots on the viewport ( only a small part of the line is visible), you will agree, that Arthur could do the job much better by clipping the line before rendering.
If you have non-solid line, you have to calculate which points on the path are to be filled and which are to be left empty. So you have to go through the whole line and calculate it. The next step is to transform the line into a painter path and finally render the path to the device. I don't know at which point does the clipping occur (btw. clipping is supported on graphics view if you set a proper hint on an item), but if it occurs after the path is calculated, there is not much that can be done. With lines it is relatively easy to clip, but if you have a complex shape, you can't just guess where to clip.

The problem in this particular application is the wrong calculation of line coordinates. The scene is more or less 1000x1000, so having coordinates that range up to several thousand in both positive and negative values yields an item that is more or less 1000 times bigger than the whole scene. And it's needlessly redrawn many times because shape() is not implemented properly.

Uwe
3rd December 2007, 11:01
I don't know at which point does the clipping occur ...
From the facts it's easy to say: at the wrong one. That's what a bug report is good for.


(btw. clipping is supported on graphics view if you set a proper hint on an item), ...

AFAIR the line was painted by QPainter from a homebrew item. In such a situation the hint gets lost in the GV framework and doesn't make it into the painting operation.


With lines it is relatively easy to clip, but if you have a complex shape, you can't just guess where to clip.

No, it's also easy to clip polygons ( f.e. Sutherland-Hodgman ) and most (all ?) shapes are ( or can be ) build from polygons. Believe me - I had to implement this all in Qwt.


The problem in this particular application is the wrong calculation of line coordinates. The scene is more or less 1000x1000, so having coordinates that range up to several thousand in both positive and negative values yields an item that is more or less 1000 times bigger than the whole scene.

Well, if you implement zooming you often run in similar situations.

Uwe

PS: Antialiasing of vertical lines is a nop. No surprise, that the performance is much better then.

wysota
3rd December 2007, 11:20
From the facts it's easy to say: at the wrong one. That's what a bug report is good for.
But you can't just move clipping to another place without affecting other things, possibly even degrading capabilities or denying the whole paint engine concept.


AFAIR the line was painted by QPainter from a homebrew item. In such a situation the hint gets lost in the GV framework and doesn't make it into the painting operation.
I'm not sure we are talking about the same thing. I'm talking about QGraphicsItem::ItemClipsChildrenToShape - it can't be lost anywhere in a custom item, because it's enforced by a parent item. So it's enough to make a transparent item that spans the whole scene to introduce clipping to the scene bounds. But I'm not really sure if it gives any performance gain, because the slowdown is caused by making a polygon, not drawing it.


No, it's also easy to clip polygons ( f.e. Sutherland-Hodgman ) and most (all ?) shapes are ( or can be ) build from polygons. Believe me - I had to implement this all in Qwt.
Yes, but first you need to have these polygons. OpenGL is so fast because it doesn't have to generate polygons itself, it receives them from the "userspace". Here you have to transform the line to a polygon first before clipping and it is the transformation that takes time. That's what I meant about the place (or time) where clipping occurs - there would be a gain only if you clipped before transforming to polygons.


Well, if you implement zooming you often run in similar situations.
No, because regardless of the zoom level, the number of logical polygons (or painter path components) doesn't change. If you have a line consisting of 100 dashes, it will always be 100 dashes - if the zoom level is 1000% or 25% that doesn't really matter.


PS: Antialiasing of vertical lines is a nop. No surprise, that the performance is much better then.
It's not nop. A line has caps, caps may be shaped differently and that causes aliasing (at least the round cap does).
http://doc.trolltech.com/latest/qt.html#PenCapStyle-enum

Furthermore Qt antialiasing is not only to remove the aliasing effect - it also softens the shape and vertical lines are affected by it - they appear thicker and blend into the background.

Uwe
3rd December 2007, 13:11
But you can't just move clipping to another place without affecting other things, possibly even degrading capabilities or denying the whole paint engine concept.

A line can always be clipped before it is painted. If not, ( I'm not sure why you try to defend Qt here ) then the design needs to be improved.


I'm not sure we are talking about the same thing. I'm talking about QGraphicsItem::ItemClipsChildrenToShape - it can't be lost anywhere in a custom item, because it's enforced by a parent item.

I was refering to the posted code. Look at EObjects::paint, where you see the QPainter::drawLine. Here the line P1,P2 (not the painter!) needs to be clipped.

OpenGL is so fast because it doesn't have to generate polygons itself, it receives them from the "userspace". Here you have to transform the line to a polygon first before clipping and it is the transformation that takes time. That's what I meant about the place (or time) where clipping occurs - there would be a gain only if you clipped before transforming to polygons.

A good example showing a situation, where the application code needs to know about the capabilities of the current paint engine. Another example is the Qt::WA_PaintOutsidePaintEvent feature ( only available with X11 ), that allows enormous performance improvements for incremental paintings.

But IMHO OpenGL is no excuse to slow down the performance of X11. F.e. introducing a Qt::WA_ClipPolygons flag would be an easy solution, that fits into the design ( guess I should send a change request myself ).



No, because regardless of the zoom level, the number of logical polygons (or painter path components) doesn't change. If you have a line consisting of 100 dashes, it will always be 100 dashes - if the zoom level is 1000% or 25% that doesn't really matter.

F.e if you zoom in a plot widget ( f.e. QwtPlot ) you want to see more details (points!) of a curve. But I'm sure you don't want to have the line-width/symbols of the curves scaled by a zoom factor.

But I'm not talking about the "logical" lines. If you zoom in a curve on a plot widget, you will soon have the "real" points very far outside the visible area.



It's not nop. A line has caps, caps may be shaped differently and that causes aliasing (at least the round cap does).
http://doc.trolltech.com/latest/qt.html#PenCapStyle-enum
Furthermore Qt antialiasing is not only to remove the aliasing effect - it also softens the shape and vertical lines are affected by it - they appear thicker and blend into the background.

Sure, but didn't you prove already, that the painting the vertical line is much faster.

The demo shows a special situation, where Qt performs bad and IMHO it's obvious why. I'm aware of similar ( f.e http://trolltech.com/developer/task-tracker/index_html?method=entry&id=160617 ) and many, many other bugs I have reported in the last half year - including a show stopper, that couldn't be fixed because of internal design problems.

Of course Qt performs better for many other situations, but is this an argument for not pointing out the problems ?

Uwe

wysota
3rd December 2007, 13:58
A line can always be clipped before it is painted. If not, ( I'm not sure why you try to defend Qt here ) then the design needs to be improved.
We have two different concepts of "clipping" here. One is clipping rasterization, another is clipping (culling?) elements to be drawn. I think you are talking about the first one and I'm talking about the second one and that's why we don't agree :) Yes, I completely agree with clipping rasterization (for example on X11 level) and if it can be done without much effort - do it. But deciding which objects are to be drawn and which are to be ignored is a totally different thing. It's done much earlier in the pipeline and can give much more benefit, yet it is much more difficult to do, because it requires using primitives and not complex shapes. So first you need to transform complex shapes into primitives and this is the task that is most time consuming. Qt draws lines not as lines, but as painter paths - so that you can use outlines, fills, etc. It is you who noticed that most time is spent transforming the path to polygons - this is not rasterization, this is still in logical coordinates! You need to transform the path to polygons, then decide which are visible and which are not, get rid of the ones that are not visible and pass the remaining ones to rasterization engine.


I was refering to the posted code. Look at EObjects::paint, where you see the QPainter::drawLine. Here the line P1,P2 (not the painter!) needs to be clipped.
Yes, I said the same thing - P1 and P2 are calculated incorrectly. But we have a simple case here - you don't care which fragments of the dotted line are "black" and which are "transparent", so recalculating P1 and P2 is very simple. But in a general case "clipping" those points needs to take into consideration that you should obtain the exact same line - the same fragments should be dots, the same should be lines and the same should be transparent, thus you have to take the characteristics of the line itself to do the recalculation. I don't think doing that on Qt level is better than your application level, because it is you who knows what the line represents, how it can be clipped and if extending the line far away beyond the scene size makes sense or not.


Another example is the Qt::WA_PaintOutsidePaintEvent feature ( only available with X11 ), that allows enormous performance improvements for incremental paintings.
Effectively disabling things like double buffering, backing store, compositions, etc. We have a situation here where you can't eat a cake and still have a cake.


But IMHO OpenGL is no excuse to slow down the performance of X11. F.e. introducing a Qt::WA_ClipPolygons flag would be an easy solution, that fits into the design ( guess I should send a change request myself ).
If you talk about sending some flag to X server, I totally agree. If you mean doing something "earlier" in the pipeline - I'm not sure. Clipping is expensive, widgets are clipped by default, I don't know if you can clip something even more just like that.


F.e if you zoom in a plot widget ( f.e. QwtPlot ) you want to see more details (points!) of a curve. But I'm sure you don't want to have the line-width/symbols of the curves scaled by a zoom factor.
But this is already implemented! The graphics view framework uses BSP trees (or tries?) to decide which items are visible and only handles those that are. Furthermore it has capabilities to ignore transformations for certain items (your symbols here). But any of these doesn't increase the logical complexity of items used. Furthermore you can use level of detail to additionally decrease complexity of items which are far away to simplify the rendering. I don't think clipping the painter has anything to do with it.


But I'm not talking about the "logical" lines. If you zoom in a curve on a plot widget, you will soon have the "real" points very far outside the visible area.
Yes, but they won't be drawn. They have to be transformed into primitives if at least a part of the shape intersects the viewport though. And there is nothing you can do about it, don't you think? When they are transformed into simple shapes, it is easy to decide which primitives need drawing and which do not. And if you want some additional culling implemented here, I totally agree (but I think it's already there).


Sure, but didn't you prove already, that the painting the vertical line is much faster.
No... I'm not sure what you are talking about... I agree that it is faster, because antialiasing is much simpler for vertical, horizontal or 45deg. sloped lines, but I don't think I proved any of this here.


The demo shows a special situation, where Qt performs bad and IMHO it's obvious why.
Yes, it's obvious why. But it's not so obvious you could improve it by modifying the paint engine.


Of course Qt performs better for many other situations, but is this an argument for not pointing out the problems ?

I immediately think about what Aaron Seigo said recently about the difference between a complaint list and a solution list :) Yes, it's easy to say "hey, it doesn't work", but it's much harder to say how to improve it. I agree that the situation we are facing is something we don't like, but I'm not sure it can be improved on Qt level with the current design. Instead I suggest to do as much as one can on application level to give Qt as little to be calculated as possible. Qt doesn't know your goal and it doesn't try to be smarter than you, thus you should be the one telling Qt how to do things in some particular case. In this situation it is enough to recompute P1 and P2 within the scene bounds which is an easy math task of finding an intersection between two known straight lines (y=ax+b and y=const). Much easier than calculating 100k polygons from a painter path.

wysota
3rd December 2007, 15:01
Here is another example, this time it calculates the proper line coordinates. You can use "," and "." keys to switch between line styles, "A" to toggle antialiasing on and off and use the mouse wheel to zoom in and out.

BTW. Notice what happens for exactly horizontal lines with non-solid style.

Uwe
3rd December 2007, 19:34
I don't think doing that on Qt level is better than your application level, because it is you who knows what the line represents, how it can be clipped and if extending the line far away beyond the scene size makes sense or not.

Agreed, but I ( the application ) only want to decide and not to implement it. Polygon clipping is the right solution for many situations and from a library with the slogan "code less..." I would like to see, that I don't have to understand and implement standard clipping algos.


Effectively disabling things like double buffering, backing store, compositions, etc. We have a situation here where you can't eat a cake and still have a cake.

But, what if one needs bread ?

More serious: I know of many Qwt applications, that are more or less controlling some strange piece of hardware. Often they are running on some low performance (sometimes old) PCs, that are dedicated to this application. F.e. I worked for a company, that sold the hardware together with the PC with the guarantee, that exactly the same combination will be delivered for a period of ten years: no chance to upgrade the graphics board.
In such an environment nobody is interested much in transparency, but if the application can't keep the refresh rates you are out of the market.

At the first Qt day ( before Qt 4.0 was out, 2003 ? ) I was representing a well known German company, what gave me the chance chance to talk to Matthias Ettrich about the performance issues ( that were much more serious with Qt4.0 ). He was very focused on this transparent desktops and was sure, that improvements of modern hardware will eliminate all problems soon. At that day I could agree with his point of view, but in the meantime we saw a new generation of fanless, low power consuming systems. And on the high end side we have 3D desktops, that are even more eye candy, than what transparency offers.

But from the "success" of Vista, I dare to say, that transparency is nice, but not a killer feature, that make users buy faster computer systems.


I immediately think about what Aaron Seigo said recently about the difference between a complaint list and a solution list :)

Speaking as author/maintainer of a library I would answer, that I'm happy to get bug reports. Being a Qt/GPL user I'm trying to support Qt by doing as much QA ( = bug reports) as possible. But I'm also responsible for a commercial product, where I'm payed for writing application code, not for finding solutions in 3rd party libraries,

Uwe

Doru
29th November 2010, 09:49
Hi,
Can you please help me with QGraphicsLineItem paint() function:

I set the pen width to 1 but the line has a width grater the 1 pixel.
Do I have to change other settings?

Thanks