Trolltech Home | Qt4-preview-feedback Home | Recent Threads | All Threads | Author | Date
All threads index page 1

Qt4-preview-feedback Archive, April 2008
Changed semantics in QGraphicsSvgItem::paint


Message 1 in thread

When overloading QGraphicsSvgItem::paint previously i had to do things like
painter->drawPixmap(pos(), ...) to get to the correct position of my item, now
it is 
painter->drawPixmap(QPoint(0,0), ...)

In my opnion that's a bad thing.

Can we have that reverted? Or will i have to #ifdef for one Qt version and 
another?

Albert

To unsubscribe - send "unsubscribe" in the subject to qt4-preview-feedback-request@xxxxxxxxxxxxx


Message 2 in thread

Albert Astals Cid wrote:
> When overloading QGraphicsSvgItem::paint previously i had to do things
> like painter->drawPixmap(pos(), ...) to get to the correct position of my
> item, now it is
> painter->drawPixmap(QPoint(0,0), ...)
> In my opnion that's a bad thing.
> Can we have that reverted? Or will i have to #ifdef for one Qt version and
> another?

Hi, Albert. Can you please show the code that worked before and not now?

-- 
 [ signature omitted ] 

Message 3 in thread

A Dimecres 09 Abril 2008, Andreas Aardal Hanssen va escriure:
> Albert Astals Cid wrote:
> > When overloading QGraphicsSvgItem::paint previously i had to do things
> > like painter->drawPixmap(pos(), ...) to get to the correct position of my
> > item, now it is
> > painter->drawPixmap(QPoint(0,0), ...)
> > In my opnion that's a bad thing.
> > Can we have that reverted? Or will i have to #ifdef for one Qt version
> > and another?
>
> Hi, Albert. Can you please show the code that worked before and not now?

I attach both the code that works with 4.4.RC1 and the one that worked on 4.3

ToDraw inherits QGraphicsSvgItem

Basically the code "cuts" the SVG when it's outside some area.

I know it's a probably subobtimal way of doing it but it works well enough for 
my use case.

As you see the only difference is the pos() call

Albert
void ToDraw::paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
void ToDraw::paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	const QRectF &bounds = transform().mapRect(boundingRect());
	const QRectF &backgroundBounds = m_background->transform().mapRect(renderer()->boundsOnElement("background"));
	double xMaxEdge = pos().x() - backgroundBounds.x() + bounds.width();
	double yMaxEdge = pos().y() - backgroundBounds.y() + bounds.height();

	double widthCut = 0;
	double heightCut = 0;
	double xStart = 0;
	double yStart = 0;
	if (xMaxEdge > backgroundBounds.width()) widthCut = xMaxEdge - backgroundBounds.width();
	if (yMaxEdge > backgroundBounds.height()) heightCut = yMaxEdge - backgroundBounds.height();
	if (pos().x() < backgroundBounds.left()) xStart = backgroundBounds.left() - pos().x();
	if (pos().y() < backgroundBounds.top()) yStart = backgroundBounds.top() - pos().y();
	if (widthCut > 0 || heightCut > 0 || xStart > 0 || yStart > 0)
	{
		QImage img(qRound(bounds.width()), qRound(bounds.height()), QImage::Format_ARGB32_Premultiplied);
		QPainter p2(&img);
		p2.setCompositionMode(QPainter::CompositionMode_Clear);
		p2.setBrush(Qt::SolidPattern);
		p2.drawRect(0, 0, qRound(bounds.width()), qRound(bounds.height()));
		p2.setCompositionMode(QPainter::CompositionMode_SourceOver);
		renderer()->render(&p2, elementId());
		p2.end();
		painter->setWorldMatrix(QMatrix());
		
		painter->drawImage(pos() + QPointF(xStart, yStart), img, QRectF(xStart, yStart, bounds.width() - widthCut, bounds.height() - heightCut));
	}
	else QGraphicsSvgItem::paint(painter, option, widget);
}
void ToDraw::paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	const QRectF &bounds = transform().mapRect(boundingRect());
	const QRectF &backgroundBounds = m_background->transform().mapRect(renderer()->boundsOnElement("background"));
	double xMaxEdge = pos().x() - backgroundBounds.x() + bounds.width();
	double yMaxEdge = pos().y() - backgroundBounds.y() + bounds.height();

	double widthCut = 0;
	double heightCut = 0;
	double xStart = 0;
	double yStart = 0;
	if (xMaxEdge > backgroundBounds.width()) widthCut = xMaxEdge - backgroundBounds.width();
	if (yMaxEdge > backgroundBounds.height()) heightCut = yMaxEdge - backgroundBounds.height();
	if (pos().x() < backgroundBounds.left()) xStart = backgroundBounds.left() - pos().x();
	if (pos().y() < backgroundBounds.top()) yStart = backgroundBounds.top() - pos().y();
	if (widthCut > 0 || heightCut > 0 || xStart > 0 || yStart > 0)
	{
		QImage img(qRound(bounds.width()), qRound(bounds.height()), QImage::Format_ARGB32_Premultiplied);
		QPainter p2(&img);
		p2.setCompositionMode(QPainter::CompositionMode_Clear);
		p2.setBrush(Qt::SolidPattern);
		p2.drawRect(0, 0, qRound(bounds.width()), qRound(bounds.height()));
		p2.setCompositionMode(QPainter::CompositionMode_SourceOver);
		renderer()->render(&p2, elementId());
		p2.end();
		painter->setWorldMatrix(QMatrix());
		
		painter->drawImage(QPointF(xStart, yStart), img, QRectF(xStart, yStart, bounds.width() - widthCut, bounds.height() - heightCut));
	}
	else QGraphicsSvgItem::paint(painter, option, widget);
}

Message 4 in thread

Albert Astals Cid wrote:
>> Hi, Albert. Can you please show the code that worked before and not now?
> I attach both the code that works with 4.4.RC1 and the one that worked on
> 4.3
> ToDraw inherits QGraphicsSvgItem
> Basically the code "cuts" the SVG when it's outside some area.
> I know it's a probably subobtimal way of doing it but it works well enough
> for my use case.
> As you see the only difference is the pos() call

Hi, Albert. You can't use pos() to draw inside paint(); the paint() function
renders in local coordinates. If you render using pos(), you'll always
shift the rendering twice relative to the position of the item (but the
painter is always translated with the item's position).

Are you able to isolate this problem with simpler code? The implementation
of paint() seems to be wrong. It looks like you're trying to implement your
own "pixmap caching", such as QGraphicsSvgItem::setCachingEnabled in Qt 4.3
or QGraphicsItem::setCacheMode in Qt 4.4. Could this be correct?

        const QRectF &bounds = transform().mapRect(boundingRect());

This maps your item's bounding rect using your item's transform. However,
this transform does not compensate for the item's position. So depending on
your item's position, bounds will be shifted.

        const QRectF &backgroundBounds =
m_background->transform().mapRect(renderer()->boundsOnElement("background"));

I assume m_background is the parent item. This code maps the local SVG's
background element bounding rect to the m_background's parent, again not
adjusting for m_background's position. Note that the parent's transform and
your item's local transform and both items' positions must be multiplied
for this transformation to work.

        double xMaxEdge = pos().x() - backgroundBounds.x() + bounds.width();
        double yMaxEdge = pos().y() - backgroundBounds.y() +
bounds.height();

These variables seem to add the local position of the item to
backgroundBounds top-left corner, I don't understand this code...

If you look at mapToItem() or mapToScene(), these functions help you map
coordinates correctly. Inside the source code of QGraphicsScene
(qgraphicsscene.cpp), you'll see in the drawItemHelper function how we
implement caching so that it works for all kinds of transforms.

I don't know why your code has regressed in Qt 4.4, it's hard to say.

-- 
 [ signature omitted ] 

Message 5 in thread

A Divendres 25 Abril 2008, Andreas Aardal Hanssen va escriure:
> Albert Astals Cid wrote:
> >> Hi, Albert. Can you please show the code that worked before and not now?
> >
> > I attach both the code that works with 4.4.RC1 and the one that worked on
> > 4.3
> > ToDraw inherits QGraphicsSvgItem
> > Basically the code "cuts" the SVG when it's outside some area.
> > I know it's a probably subobtimal way of doing it but it works well
> > enough for my use case.
> > As you see the only difference is the pos() call
>
> Hi, Albert. You can't use pos() to draw inside paint(); the paint()
> function renders in local coordinates. If you render using pos(), you'll
> always shift the rendering twice relative to the position of the item (but
> the painter is always translated with the item's position).

That's not true, it is translated now, it was not in Qt 4.3.

>
> Are you able to isolate this problem with simpler code? 

Of course i can, it's quite easy. Here it goes, along with the svg and the 
screenies of what happens in Qt 4.3 and 4.4-rc1

That behaviour change is not an issue to me anymore as i changed the way of 
doing what i did in the paint method and works on 4.3 and 4.4 now, but you 
should really be more careful when changing what parameters mean.

Albert


> The implementation 
> of paint() seems to be wrong. It looks like you're trying to implement your
> own "pixmap caching", such as QGraphicsSvgItem::setCachingEnabled in Qt 4.3
> or QGraphicsItem::setCacheMode in Qt 4.4. Could this be correct?
>
>         const QRectF &bounds = transform().mapRect(boundingRect());
>
> This maps your item's bounding rect using your item's transform. However,
> this transform does not compensate for the item's position. So depending on
> your item's position, bounds will be shifted.
>
>         const QRectF &backgroundBounds =
> m_background->transform().mapRect(renderer()->boundsOnElement("background")
>);
>
> I assume m_background is the parent item. This code maps the local SVG's
> background element bounding rect to the m_background's parent, again not
> adjusting for m_background's position. Note that the parent's transform and
> your item's local transform and both items' positions must be multiplied
> for this transformation to work.
>
>         double xMaxEdge = pos().x() - backgroundBounds.x() +
> bounds.width(); double yMaxEdge = pos().y() - backgroundBounds.y() +
> bounds.height();
>
> These variables seem to add the local position of the item to
> backgroundBounds top-left corner, I don't understand this code...
>
> If you look at mapToItem() or mapToScene(), these functions help you map
> coordinates correctly. Inside the source code of QGraphicsScene
> (qgraphicsscene.cpp), you'll see in the drawItemHelper function how we
> implement caching so that it works for all kinds of transforms.
>
> I don't know why your code has regressed in Qt 4.4, it's hard to say.
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsSvgItem>

class ToDraw : public QGraphicsSvgItem
{
	public:
		ToDraw() : QGraphicsSvgItem("norway.svg")
		{
		}
		
		void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget )
		{
			QGraphicsSvgItem::paint(painter, option, widget);
			
			painter->setPen(QPen(Qt::black));
			painter->setBrush(QColor(Qt::black));
			painter->drawRect(0, 0, 10, 10);
		}
};


int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	
	QGraphicsView *gv = new QGraphicsView();
	QGraphicsScene *scene = new QGraphicsScene();
	gv->setScene(scene);
	gv->show();
	gv->resize(800, 600);
	
	ToDraw *item = new ToDraw;
	item->setPos(QPoint(0,0));
	item->setZValue(0);
	scene->addItem(item);

	return a.exec();
}

Attachment:

Attachment: norway.svg
Description: image/svg

PNG image

PNG image


Message 6 in thread

Albert Astals Cid wrote:
>> Are you able to isolate this problem with simpler code?
> Of course i can, it's quite easy. Here it goes, along with the svg and the
> screenies of what happens in Qt 4.3 and 4.4-rc1
> That behaviour change is not an issue to me anymore as i changed the way
> of doing what i did in the paint method and works on 4.3 and 4.4 now, but
> you should really be more careful when changing what parameters mean.

The difference seems to be that QGraphicsSvgItem::paint leaves the painter
in a translated state in 4.4, whereas in 4.3 it didn't. Wrapping the call
to QGraphicsSvgItem::paint inside a QPainter::save/restore pair should work
around the bug.

Yes, it seems to be a bug, we'll try to fix it as soon as possible.

-- 
 [ signature omitted ] 

Message 7 in thread

Andreas Aardal Hanssen wrote:
> The difference seems to be that QGraphicsSvgItem::paint leaves the painter
> in a translated state in 4.4, whereas in 4.3 it didn't. Wrapping the call
> to QGraphicsSvgItem::paint inside a QPainter::save/restore pair should
> work around the bug.
> Yes, it seems to be a bug, we'll try to fix it as soon as possible.

On closer inspection, the translation bug that this example shows seems to
be a bugfix, not a regression. The painter was previously left in a funny
state, but now it is no longer. If you run this example, which renders
before and after the call to QGraphicsSvgItem::paint(), you'll see that the
two rects are rendered at the right spot in 4.4, but in 4.3 (uncomment the
cache line in the constructor) the box is drawn in device coordinates.

The cache mode implementation in QGraphicsSvgItem previously left the
painter in device space, but as of 4.4, the painter state is properly
restored.

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsSvgItem>

class ToDraw : public QGraphicsSvgItem
{
        public:
                ToDraw() : QGraphicsSvgItem("norway.svg")
                {
                        setCacheMode(NoCache);
                }
                
                void paint ( QPainter * painter, const
QStyleOptionGraphicsItem * option, QWidget * widget )
                {
                        painter->setPen(QPen(Qt::blue));
                        painter->setBrush(QColor(Qt::blue));
                        painter->drawRect(-10, -10, 20, 20);

                        QGraphicsSvgItem::paint(painter, option, widget);
                        
                        painter->setPen(QPen(Qt::black));
                        painter->setBrush(QColor(Qt::black));
                        painter->drawRect(0, 0, 10, 10);

                }
};


int main(int argc, char *argv[])
{
        QApplication a(argc, argv);
        
        QGraphicsView *gv = new QGraphicsView();
        QGraphicsScene *scene = new QGraphicsScene();
        gv->setScene(scene);
        gv->show();
        gv->resize(800, 600);
        
        ToDraw *item = new ToDraw;
        item->setPos(QPoint(0,0));
        item->setZValue(0);
        scene->addItem(item);

        return a.exec();
}

-- 
 [ signature omitted ] 

Message 8 in thread

A Dimarts 29 Abril 2008, Andreas Aardal Hanssen va escriure:
> Andreas Aardal Hanssen wrote:
> > The difference seems to be that QGraphicsSvgItem::paint leaves the
> > painter in a translated state in 4.4, whereas in 4.3 it didn't. Wrapping
> > the call to QGraphicsSvgItem::paint inside a QPainter::save/restore pair
> > should work around the bug.
> > Yes, it seems to be a bug, we'll try to fix it as soon as possible.
>
> On closer inspection, the translation bug that this example shows seems to
> be a bugfix, not a regression. 

Ok, thanks for dedicating so much time to me :-)

Good i'm no longer using a bug to get the behaviour i wanted.

Albert

> The painter was previously left in a funny 
> state, but now it is no longer. If you run this example, which renders
> before and after the call to QGraphicsSvgItem::paint(), you'll see that the
> two rects are rendered at the right spot in 4.4, but in 4.3 (uncomment the
> cache line in the constructor) the box is drawn in device coordinates.
>
> The cache mode implementation in QGraphicsSvgItem previously left the
> painter in device space, but as of 4.4, the painter state is properly
> restored.
>
> #include <QApplication>
> #include <QGraphicsView>
> #include <QGraphicsSvgItem>
>
> class ToDraw : public QGraphicsSvgItem
> {
>         public:
>                 ToDraw() : QGraphicsSvgItem("norway.svg")
>                 {
>                         setCacheMode(NoCache);
>                 }
>
>                 void paint ( QPainter * painter, const
> QStyleOptionGraphicsItem * option, QWidget * widget )
>                 {
>                         painter->setPen(QPen(Qt::blue));
>                         painter->setBrush(QColor(Qt::blue));
>                         painter->drawRect(-10, -10, 20, 20);
>
>                         QGraphicsSvgItem::paint(painter, option, widget);
>
>                         painter->setPen(QPen(Qt::black));
>                         painter->setBrush(QColor(Qt::black));
>                         painter->drawRect(0, 0, 10, 10);
>
>                 }
> };
>
>
> int main(int argc, char *argv[])
> {
>         QApplication a(argc, argv);
>
>         QGraphicsView *gv = new QGraphicsView();
>         QGraphicsScene *scene = new QGraphicsScene();
>         gv->setScene(scene);
>         gv->show();
>         gv->resize(800, 600);
>
>         ToDraw *item = new ToDraw;
>         item->setPos(QPoint(0,0));
>         item->setZValue(0);
>         scene->addItem(item);
>
>         return a.exec();
> }

To unsubscribe - send "unsubscribe" in the subject to qt4-preview-feedback-request@xxxxxxxxxxxxx