PDA

View Full Version : QGraphicsPixmapItem performance



cerber
9th August 2007, 10:44
Hi.

I'm new to here, and my English may be bad, so, please, don't pay attention to it.

Here is my problem:

I've created a QGraphicsPixmapItem with a big (~4500/3600 pix) pixmap. When it's not scaled, the scrolling of the QGraphicsView is very fast. When it's scaled (no matter how much - 1.01 or 2.0 times, etc), scrolling it VERY slow (about 1-2 seconds per each click on the slider).

No matter how to scale: view->scale() or pixmapItem->scale()

As far as I understand, Qt should repaint only the visible regions. Which means that if I make a visible region smaller, scrolling will be fasted.

However, if I make my window very small (eg. 100x100 pix), scrolling is still very slow. Which makes me think that Qt repaints (and scales) not only the visible region.

My PC is Celeron 2.4, 512 Mb, Qt 4.2.3, Mandriva 2007

Could you please help me, or point any existing topic on this issue.

Thanks!

marcel
9th August 2007, 21:46
Have you played with QGraphicsView::OptimizationFlags and QGraphicsView::ViewportUpdateMode?

Regards

cerber
10th August 2007, 06:19
Unfortunately, I don't have any of these in my Qt 4.2.3 :(

As far as I understood, both of them were introduced in Qt 4.3

Still, Thanks for an advice.

cerber
10th August 2007, 10:19
I've installed Qt 4.3 and tried these.

It makes very slight difference, but not what I expect.

Do you have any other thoughts?

Thanks.

wysota
10th August 2007, 10:28
In my opinion it is the scaling that takes that long. Try different transform modes (Fast and Smooth) and see if it makes a difference.

cerber
10th August 2007, 14:52
For sure it's scaling that takes so long.

I've started from Fast mode, which has already been slow, now I have to use Smooth since it's one of the requirements. Smooth and Fast make not much difference.

Now I'm trying to make some time measurements in the paint() method of the view.
It seems like there is some expence on scaling, which depends linearly on the size of the whole image, but not the rect to be redrawn (scaled).

The bigger the image I load, the slower the scrolling. However, I'd say that scaling a part (rect) of an image "on-the-fly" should depend on the size of the rect, but not the whole image.

I guess I'll try to handle updates on my own to ensure I update a part of an image, or use my own algorithm to scale, and see if it makes difference...

marcel
10th August 2007, 15:01
Maybe this is the way the view paints items.
What about splitting the bigger image in smaller images, add all of them to the scene so they appear they form a single image(even in an item group, to make them behave as one )?
This way when you scroll or scale, the view will update only visible items. The other tiles that are out of view won't be scaled/updated.

Regards

wysota
10th August 2007, 15:16
The pixmap will be scaled during every repaint because in reality it is not the pixmap item that is scaled, but the painter and it gets scaled on every repaint. My suggestion is not to use a large pixmap at all or optimize the item, for example by composing it of more (4, 9, 16) smaller pixmaps. This way smaller pixmaps will be scaled and only those that are actually visible in the view.

cerber
10th August 2007, 15:17
Good idea! Thanks.

I'll try this, for sure.

cerber
10th August 2007, 15:21
The pixmap will be scaled during every repaint because in reality it is not the pixmap item that is scaled, but the painter and it gets scaled on every repaint.

Well, using drawPixmap() with target rect being a part of a whole image should make painter draw (and scale) only a part of it...

Still, splitting a big pixmap is a good idea. I'll try it ob monday, I guess.

spud
10th August 2007, 16:41
Before you try optimizing low level rendering routines, try the following program:


#include <QtGui>

int main(int argc, char* argv[])
{
QApplication app(argc, argv);
if(argc<2){
QMessageBox::information(0, "No Arguments provided", "Usage: bigimage <imagefile> [scaleToX scaleToY]");
return 0;
}
QPixmap pix(argv[1]);
if(argc>3)
pix=pix.scaled(atoi(argv[2]), atoi(argv[3]));

QGraphicsScene scene;
scene.addPixmap(pix)->scale(1.1,1.1);
QGraphicsView view(&scene);
view.show();
return app.exec();
}

I didn't have any really big images so I scaled a smaller one before adding it to the scene.


./bigimage image.tif 10000 10000

On windows with Qt 4.3.0 scrolling was smooth as butter with a 10,000 x 10,000 image. Perhaps your problem lies elsewhere?

marcel
10th August 2007, 21:25
Before you try optimizing low level rendering routines, try the following program:

This really depends on the machine performances.
I am sure that using tiles will move even faster on a fast computer.

Anyway, he's not gonna optimize nothing. He will just implement a drawing mechanism.

Regards

cerber
13th August 2007, 09:34
I didn't have any really big images so I scaled a smaller one before adding it to the scene.

That's the point!
I don't need to prepare a big image before adding it to scene, I need Qt to scale it "on-the-fly" while scrolling.

Think about an amount of memory required to store an image e.g. 4500x3500 with 32 bit depth scaled e.g. by 3.0 . It's over 550 Mb, which is unexceptable. Not to speak about storing two such pixmaps, required to draw my scene...

I'm working on splitting pixmap, the results will be soon.

wysota
13th August 2007, 10:11
It's 540MB to be precise :) But you have a wrong assumption. Pixmaps are stored in a compressed state so only the part that is actually drawn should be decompressed. But the first thing you could try is to get rid of the alpha channel and use 25b (24b of colour and a single bit transparency mask) or even 24b (no alpha). Of course splitting the image as suggested earlier is the first thing to try. It should really help much.

cerber
13th August 2007, 10:42
My precision is 4500*3500*3*3*4 = 567 Mb :)

I haven't heard anything about compressed state of pixmaps. Where can I get more info on that?

By the may, simply using "top" utility gives me a reason to think that pixmaps are not compressed. Of course, I might be wrong.

I can't use 24 or 25 bit color, since I need semi-transparency.

It's the first time I hear about 25 bit pixmaps in Qt. Where can I get more info on that?

Thanks.

wysota
13th August 2007, 12:52
My precision is 4500*3500*3*3*4 = 567 Mb :)
1MB = 1024 * 1024 B so you should divide your result by 1.048 which will give circa 540.7MB.


I haven't heard anything about compressed state of pixmaps. Where can I get more info on that?
Google? :) Or X.org or something similar... Pixmaps are generally images stored on the X server (so they are probably compressed).


It's the first time I hear about 25 bit pixmaps in Qt. Where can I get more info on that?

There are no 25bit pixmaps. The bitmap is 24b deep and you additionally apply a 1 bit mask (like using QPixmap::setMask) which gives a total of 25 bits. Of course you can't apply the mask directly.

spud
13th August 2007, 13:59
That's the point!
I don't need to prepare a big image before adding it to scene, I need Qt to scale it "on-the-fly" while scrolling.

Think about an amount of memory required to store an image e.g. 4500x3500 with 32 bit depth scaled e.g. by 3.0 . It's over 550 Mb, which is unexceptable. Not to speak about storing two such pixmaps, required to draw my scene...

I'm working on splitting pixmap, the results will be soon.

I'm afraid you missed the point. I only provided the means of scaling the pixmap for people like me, who don't happen to have 10,000x10,000 images lying around. I add the pixmap to the scene and then I scale the Item by (1.1, 1.1). For all intents and purposes this is the same as loading a 10,000x10,000 image to begin with. It also doesn't matter if i scale the item, the view or the scene. The example runs perfectly smooth on my low end PC(using about 400MB of RAM, but thats expected since 10 000 * 10 000 * (25 bits) = ~300 MB).
So, could you try the example with your image and tell us how it works. If
a) It runs smoothly: You seem to have a problem elsewhere in your code.
or
b) Scrolling is as slow as before: The problem would be Qt 4.2.3, where the whole image is scaled, regardless of the viewport.

I am not saying that splitting the image isn't a good idea. Especially in conjuncture with the QPixmapCache, in case the whole image won't fit to memory. But a 4500x3000 image really isn't that large(~40MB) and should work smoothly even on a very low end PC.

use the following extended example to test the speed of "live" scaling with the mousewheel.

#include <QtGui>

class GraphicsView : public QGraphicsView
{
public:
GraphicsView(QGraphicsScene* scene=0)
: QGraphicsView(scene)
{
}
virtual void wheelEvent(QWheelEvent* event)
{
if (event->delta()>0)
scale(2.0, 2.0);
else
scale(0.5, 0.5);
}
};

int main(int argc, char* argv[])
{
QApplication app(argc, argv);
if(argc<2){
QMessageBox::information(0, "No Arguments provided", "Usage: bigimage <imagefile> [scaleToX scaleToY]");
return 0;
}
QPixmap pix(argv[1]);
if(argc>3)
pix=pix.scaled(atoi(argv[2]), atoi(argv[3]));

QGraphicsScene scene;
scene.addPixmap(pix);
GraphicsView view(&scene);
view.show();
return app.exec();
}



regards

cerber
13th August 2007, 14:46
I'm afraid you missed the point. I only provided the means of scaling the pixmap for people like me, who don't happen to have 10,000x10,000 images lying around.

Sorry, I did miss what you ment.

However, no matter what example I run, no matter Qt 4.2.3 or Qt 4.3 it is, scrolling the scaled image is as slow as I told before. I guess that Windows may behave very differently, so you don't see my problem.

There is no problem elsewhere in my code.

I think that splitting the pixmap will make the difference.

Thanks for your help.


To Wysota: Thanks for explanations.

spud
13th August 2007, 14:53
But you have a wrong assumption. Pixmaps are stored in a compressed state so only the part that is actually drawn should be decompressed.

That doesn't seem to be the case, at least not for all platforms:
QGrapicsPixmapItem has a member QPixmap pixmap;
QPixmap has a member QImage image;
QImage has a member unsigned char data.
data is an array the size of width x height x depth.

The following is the memory footprint of a QPixmap on windows:


[member] [value] [type]
--------------------------------------------------------------------------
-d 0x00b24108 QGraphicsPixmapItemPrivate * const
pixmap {...} QPixmap
-data 0x00b13430 QPixmapData *
count 2 int
detach_no 0 int
-image {...} QImage
-d 0x00b26e58 QImageData *
+ref {...} QAtomic
width 1300 int
height 1300 int
depth 32 int
nbytes 6760000 int
+colortable {...} QVector<unsigned int>
+data 0x00c20040 unsigned char*
...

cerber
13th August 2007, 15:06
That doesn't seem to be the case, at least not for all platforms:

Wysota was talking about X server, which has no meaning for Windows.

wysota
14th August 2007, 11:56
Wysota was talking about X server, which has no meaning for Windows.

Exactly. Pixmaps are implemented as images on Windows. It should make a difference on X11 though. It should be quite easy to test using a remote X server and a big pixmap.

cerber
22nd August 2007, 14:41
Hi.

I've tested the approach with splitting pixmaps. It made a slight difference, but didn't seem the result that I expected.

Finally, I've come to a thought that I have to zoom the image myself. This way:

1. I reimplemented the paintEvent of the view.
2. I look at the region that comes with event.
3. For each rect of that region, I make a buffer of the size of the rect
4. Copy a part of an image to that buffer usign image->bits()
5. Pass that buffer to a function that scales an image (this function was implemented by my colleague).
6. Then, create a QImage of returned buffer.
7. And finally, draw that image on the target rect.

Unbelivably, this works MUCH faster then Qt's pixmap transformation.

The problem left is that very often Qt sends a paintEvent with the update-region equal to the whole view (which seems unneeded), and this slows this method a bit.

But still - it's about 5-10 times faster!

Thanks to everyone for your advices.