PDA

View Full Version : How to speed up a transparent QGraphicsTextItem in Opengl?



JohannesMunk
27th October 2009, 14:41
Hi there!

I'm trying to increase the rendering speed of a transparent QGraphicsTextItem. Buffering the output in a pixmap using the GraphicsItem CacheMode doesn't work for me, because the scenes perspective changes a lot and with it the background behind the transparent text.

So I thought of compiling an opengl-displaylist of the QGraphicsTextItem.paint. Did anyone already try that?

My problem is that before I can call paint myself, I need to setup a QStyleOptionGraphicsItem which seems like a complex undertaking. Is there a simple way?

Thx in advance!

Johannes

wysota
27th October 2009, 16:32
I'm trying to increase the rendering speed of a transparent QGraphicsTextItem. Buffering the output in a pixmap using the GraphicsItem CacheMode doesn't work for me, because the scenes perspective changes a lot and with it the background behind the transparent text.
The background is irrelevant. The cache contains the item only - without its background. The cache would only be useless if you constantly changed items themselves. In other situations at least one of the cache modes should introduce speedup.


So I thought of compiling an opengl-displaylist of the QGraphicsTextItem.paint. Did anyone already try that?
That's essentially what Qt does automatically if you use OpenGL viewport and do caching. It keeps the pixmap in memory of your gfx card and just blits it to the framebuffer.


My problem is that before I can call paint myself, I need to setup a QStyleOptionGraphicsItem which seems like a complex undertaking. Is there a simple way?

Contents of the style option object depends on the state of the view so the best you can do is to rerun paint everytime this object changes which is basically what Qt already does with caching.

JohannesMunk
27th October 2009, 18:29
First of all: Thx for your fast response!


The background is irrelevant. The cache contains the item only - without its background. The cache would only be useless if you constantly changed items themselves. In other situations at least one of the cache modes should introduce speedup.

Is this achieved by alphablending the pixmap/texture?


That's essentially what Qt does automatically if you use OpenGL viewport and do caching. It keeps the pixmap in memory of your gfx card and just blits it to the framebuffer.

Caching a pixmap is not what I had in mind when I spoke of an OpenGL-Displaylist. I sort of hoped to compile the actual vertex ops etc..


Contents of the style option object depends on the state of the view so the best you can do is to rerun paint everytime this object changes which is basically what Qt already does with caching.
Yes, so far that's clear. But if I would want to render the TextItem once outside a paintevent to compile a displaylist myself, I need to pass a valid style option. How do I get that?

Probably my problem with the cachemodes is that for nearly each repaint (camera position changed) I'm changing the projection of all items. My old 3d mapping thing.. (http://www.qtcentre.org/forum/f-qt-programming-2/t-qgraphicsscene-widgets-in-opengl-projection-with-only-a-tiny-problem-20059.html)

Where in the Qt sources do I find the caching modes implementation for the opengl painter?

Thx again!

Johannes

wysota
27th October 2009, 22:42
Is this achieved by alphablending the pixmap/texture?
Yep, something like that.


Caching a pixmap is not what I had in mind when I spoke of an OpenGL-Displaylist. I sort of hoped to compile the actual vertex ops etc..
The text is most likely rasterized first so you'd get four vertices and a texture. You get the same for a pixmap.


Yes, so far that's clear. But if I would want to render the TextItem once outside a paintevent to compile a displaylist myself, I need to pass a valid style option. How do I get that?
You need to fill one properly but then there is a question what would you fill in there. I assure you - you won't do better than what Qt already does in this field. You can't do better than blitting a texture into the buffer.


Probably my problem with the cachemodes is that for nearly each repaint (camera position changed) I'm changing the projection of all items.
You should be able to use ItemCoordinateCache with this approach. As long as you modify the transformations of items and not their internals, this should work.


Where in the Qt sources do I find the caching modes implementation for the opengl painter?
Either in src/gui/graphicsview/qgraphicsitem* or in src/opengl/qpaintengine_opengl* and src/opengl/qpixmapdata_gl*. I assume the bare caching code is the same regardless of the backend - it just stores an appropriate pixmap. The magic is how the pixmap works.

JohannesMunk
27th October 2009, 23:48
Thx for your help, but using ItemCoordinateCache just gives me a solid black background. I suppose when the text-background is cached my background still is black.

I will investigate this further, when there is more time for it. Just thought I might ask and rule out any obvious solutions.

Gn8!

Johannes

wysota
28th October 2009, 00:14
Thx for your help, but using ItemCoordinateCache just gives me a solid black background.

How exactly do you activate the cache?

JohannesMunk
28th October 2009, 00:22
In the constructor of my ProjectionItem I create the textchilditem:



_textitem = new QGraphicsTextItem(text,this);
...
_textitem->setCacheMode(QGraphicsItem::ItemCoordinateCache);

wysota
28th October 2009, 00:34
And what is the boundingRect() of the item?

Edit: I'm not able to reproduce the behaviour you observe using the following code. Can you change it so that it reproduces your problem?


#include <QtGui>
#include <QGLWidget>

int main(int argc, char **argv){
QApplication app(argc, argv);
QGraphicsView view;
QGraphicsScene scene;
scene.setBackgroundBrush(Qt::blue);
view.setScene(&scene);
view.setViewport(new QGLWidget);
QString txt = "Some text";
QGraphicsTextItem *item = scene.addText(txt);
QFont f;
f.setPointSize(36);
item->setFont(f);
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QTransform trans;
trans.rotate(60, Qt::YAxis);
item->setTransform(trans);
item->setOpacity(0.7);
view.show();
return app.exec();
}

JohannesMunk
28th October 2009, 00:45
The BoundingRect for the ProjectedItem is

QPainterPath CGL3dProjectedItem::shape() const
{
QPainterPath p;
QPolygonF polygon = mapToScene(this->childrenBoundingRect());
p.addPolygon(polygon);
return p;
}
virtual QRectF boundingRect() const {return _bounds;}

in UpdateTransformation:
..
_bounds = shape().boundingRect();
..

The black area is fine. Exactly the area of the textitem. And the text shows as it should with transparent background as soon as I disable the caching.

I do really appreciate your efforts!

JohannesMunk
28th October 2009, 00:55
I have seen your example. Added a gradient to the background and it still works. Amazing :->

I don't know exactly, where my problem comes in. With the transformation or the way I'm drawing the background. I am drawing the background directly with gl. Maybe thats the problem.

I will implement something simple but similiar and report back.

wysota
28th October 2009, 01:03
Your implementation of shape() and boundingRect() is awfully slow. You are creating painter paths all the time. Cache all the data you can (meaning the shape and the bounding rect), this should already give you some speed improvement.

BTW. The background shouldn't influence the contents of the item's cache... I suspect this is caused by an incorrect result of boundingRect() at the time the cache is being enabled.

JohannesMunk
28th October 2009, 01:04
Well the GL Background isn't it:


#include <QtGui>
#include <QGLWidget>

class CGLScene : public QGraphicsScene
{
protected:
void drawBackground(QPainter *painter, const QRectF &rect)
{
glClearColor(1.0,0.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
};

int main(int argc, char **argv){
QApplication app(argc, argv);
QGraphicsView view;
CGLScene scene;

view.setScene(&scene);
view.setViewport(new QGLWidget);
QString txt = "Some text";
QGraphicsTextItem *item = scene.addText(txt);
QFont f;
f.setPointSize(36);
item->setFont(f);
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setCacheMode(QGraphicsItem::ItemCoordinateCache);
QTransform trans;
trans.rotate(60, Qt::YAxis);
item->setTransform(trans);
item->setOpacity(0.7);
view.show();
return app.exec();
}



Problem is, introducing the perspective would blow this up a lot. We would need the camera, the projecteditem etc.. I could integrate the TextItem into the ProjectionDemo I did some time ago.. But than it would be a lot of code for you to read..

JohannesMunk
28th October 2009, 01:08
Your implementation of shape() and boundingRect() is awfully slow. You are creating painter paths all the time. Cache all the data you can (meaning the shape and the bounding rect), this should already give you some speed improvement.

Yes, I know. But that's definitely not the speed problem. It makes no difference if I have just one TextItem (thus only one transformation, shape, boundingrect calculation) with a lot of text or lots of textitems with just one line of text. Both are equally slow.



BTW. The background shouldn't influence the contents of the item's cache... I suspect this is caused by an incorrect result of boundingRect() at the time the cache is being enabled.

That is definitely the case! The cache is enabled in the constructor. The boundingrect changes all the time. Through the perspective change..

We are on to something here :->

JohannesMunk
28th October 2009, 01:14
Mmh.. What can I do about it?

The ProjectedItem changes its projection. And thus its boundingrect.

I don't set the boundingrect of the textitem at all. I just leave it as it is. Because its a childitem of the projecteditem it gets drawn at the right place.

The ProjectedParentItem has no caching activated.

When would be the right time to activate caching?

JohannesMunk
28th October 2009, 01:51
Hey Wysota!

I took the time and added the TextItem to the Widget3D Demo.

http://upload.convis.info/Data/Widget3d_3.jpg

So whenever you have the time you are welcome to look at the code. If not, it'll stay that slow a little longer :->

Most relevant file is widget3d.h (CGL3dProjectedItem, CGL3dTextItem, ..).

There you can disable the cachemode. And it works just fine.

I think the core problem is: How can I use caching when the transformation and thus the boundingrect changes all the time?

Thx a lot for your help so far!

Johannes

wysota
28th October 2009, 02:11
You have to isolate why the background is black. You can see it is not black in my code so there is nothing wrong with the cache in general. As for your bounding rect - see that when your item has no children its bounding rect will be invalid. If you create the cache at that time and not pass the amount of memory you want allocated for the item's cache, it will allocate as much memory as needed to fit your bounding rect. Which in your case is... 0. This is likely to lead to problems although I'd expect everything to be black, not just the background.

I suggest you start from scratch (or with my code) and build upon it until you encounter a situation where cache fails. It might take shorter than wild bug chasing.

JohannesMunk
28th October 2009, 02:17
Caching is only enabled for the TextItem! That is a child to a projecteditem, whose shape/boundingrect code I have given you earlier. The caching of the TextItem should not be affected by that, or is it?

The TextItems-Bounding-Rect seems to be fine, as the cache-related black background suggests.

wysota
28th October 2009, 10:25
So is everything alright now or not?

JohannesMunk
28th October 2009, 11:45
Hi there!

I'm afraid it is not. The solid black background stays in my application. As you suggested, I continued our minimal error example in introducing the 3DTextItem and a dummy ProjectedItem. Sadly, still works.

main.cpp:


#include <QtGui>
#include <QGLWidget>
#include "textitem.h"

class CGLScene : public QGraphicsScene
{
protected:
void drawBackground(QPainter *painter, const QRectF &rect)
{
glClearColor(1.0,1.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
};

int main(int argc, char **argv){
QApplication app(argc, argv);
QGraphicsView view;
CGLScene scene;

view.setScene(&scene);
view.setViewport(new QGLWidget);

CGL3dTextItem* item = new CGL3dTextItem("");
item->setHTML("<font color=#FF0000>FAT TEST </font>");

scene.addItem(item);

QFont f;
f.setPointSize(36);
item->setFont(f);
item->setFlag(QGraphicsItem::ItemIsMovable);
//item->setCacheMode(QGraphicsItem::ItemCoordinateCache);

QTransform trans;
trans.rotate(60, Qt::YAxis);
item->setTransform(trans);
item->setOpacity(1);
view.show();
return app.exec();
}


textitem.h - a separate header file for moc to start up.

#ifndef TEXTITEM_H
#define TEXTITEM_H

#include <QtGui>
#include <QObject>

class CGL3dProjectedItem : public QObject, public QGraphicsItem
{ Q_OBJECT
protected:
virtual QRectF boundingRect() const {return shape().boundingRect();}
virtual QPainterPath shape() const {QPainterPath p;QPolygonF polygon = mapToScene(this->childrenBoundingRect());p.addPolygon(polygon);retu rn p;}
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) {}
};

class CGL3dTextItem : public CGL3dProjectedItem
{ Q_OBJECT
Q_PROPERTY(QObject* textitem READ getTextItem)
Q_PROPERTY(QString text READ getText WRITE setText)
Q_PROPERTY(QString html READ getHTML WRITE setHTML)
Q_PROPERTY(QFont font READ getFont WRITE setFont)
public:
CGL3dTextItem(QString text) : CGL3dProjectedItem()
{
_textitem = new QGraphicsTextItem(text,this);
_textitem->setTextInteractionFlags(Qt::TextBrowserInteraction );
QTextOption textoption(Qt::AlignCenter);
textoption.setWrapMode(QTextOption::WrapAtWordBoun daryOrAnywhere);
_textitem->document()->setDefaultTextOption(textoption);
_textitem->setCacheMode(QGraphicsItem::ItemCoordinateCache);
//setHandlesChildEvents(true);
}
enum { Type = UserType+2};
int type() const { return Type;}
QObject* getTextItem() {return _textitem;}
QString getText() {return _textitem->toPlainText();}
void setText(QString plaintext) {_textitem->setPlainText(plaintext);TextChanged();}
QString getHTML() {return _textitem->toHtml();}
void setHTML(QString htmltext) {_textitem->setHtml(htmltext);TextChanged();}
QFont getFont() {return _textitem->font();}
void setFont(QFont f) {_textitem->setFont(f);TextChanged();}
protected:
void TextChanged()
{
// SetCenterPos(CGL3d(-_textitem->document()->size().width()/2,0,0));
//qDebug() << "TextItem Center: " << CenterPos().toString();
}
private:
QGraphicsTextItem* _textitem;
};

#endif // TEXTITEM_H


So maybe the problem is hidden in the kind of transformation I am doing. I will investigate.

Thx

Joh