Qt | QPixmap scaling incorrectly - c++

I have a QPixmap and I set it to resize according to ratio as the window is resized. When the images is first loaded in, it is good and clear for that ratio, but as I resize the image, it distorts all of it.
It manages to go from this:
to this:
Relevant Code:
void MainWindow::resizeEvent(QResizeEvent *)
{
QPixmap pix = ui->labelImage->pixmap()->scaled(ui->labelImage->size(),
Qt::KeepAspectRatio);
ui->labelImage->setPixmap(pix);
}

You're reading the current pixmap from the widget, scaling it, and then writing the scaled pixmap to the widget. Consider what happens if the widget becomes very small and is then resized to something much larger -- you'll get a lot of artifacts due to the scaling transformation used.
I think a better approach would be to store the original full size image in a member of your MainWindow class, say...
QPixmap m_original_pixmap;
Then use a scaled version of that in your resizeEvent member...
void MainWindow::resizeEvent(QResizeEvent *)
{
QPixmap pix = m_original_pixmap.scaled(ui->labelImage->size(), Qt::KeepAspectRatio);
ui->labelImage->setPixmap(pix);
}
Not sure if that will clear everything up but should go some way to removing some of the artifacts.
As a side note, if you're concerned about image quality you might consider specifying Qt::SmoothTransformation as the pixmap transformation mode in the scaling operation.

Related

Is there any way to smoothly scale and draw a section of a QPixmap that's defined by a QRectF?

I'm working on a Qt 5 mac application and I'm looking for a method to smoothly scale and render a fractional section of a QPixmap as defined by a QRectF.
What I want to do actually is exactly what void QPainter::drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect) is supposed to do:
QRectF sourceRect = ...;
QRectF targetRect = ...;
painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing);
painter.drawPixmap(targetRect, pixmap, sourceRect);
However, as mentioned in this thread and this bug report, QPainter::SmoothPixmapTransform is ignored and this method does not work.
Short of writing my own efficient bilinear scaling method from scratch (which I am not inclined to do), is there any other method I can use to accomplish the same thing?
QPixmap::scaled() only allows scaling to integer dimensions, and provides no way to crop out a section defined by a QRectF, so that's a non-starter.
Just tossing out ideas here, but is it possible to somehow use OpenGL to render the portion of the image I want at the right scale and then render it back into a QImage or QPixmap? This kind of scaling is one of the most basic things OpenGL can do very easily and quickly, but I don't have any experience using OpenGL in a Qt desktop app so far so I'm not sure if this is possible.

Fast QPixmap scaling

I develop a browser-like application, where the canvas has large height and "ordinary" width, something like 1024x999999. I display a picture using 512 cached QPixmap blocks (1024x128), re-using them to display newly drawing areas. So if user scrolls around some given area of the large image, CPU is not busy, the cached blocks is used. So, this is how my engine works, briefly.
Want to implement a zoom. Don't know - smooth or discrete (x2, x3, x4...). Performance questions:
Is there any effective way to scale QPixmap on-the-fly in paintEvent() without allocating too much memory?
or maybe i should think about "zoom-layers" that cache zoomed picture for different zoom factors? But this makes smooth zooming impossible...
If you take a look at the documentation, you'll see that the paintEvent in fact receives a QPaintEvent object. This object has a getter method named region() that returns a QRect detailing the region to be repainted.
void QWidget::paintEvent ( QPaintEvent * event )
{
QRect region = event->region();
...
}
So... you just need to repaint the part of the widget that is exactly inside that rectangle.
For your application, I recommend to calculate which image or images are within the rectangle, and redraw them accordingly, but only those images.
For the zoom part, Qt has optimized the way images are painted in QPainter objects if images are QPixmap objects. Or so they say...
So, you can write inside the paintEvent() method something like:
QPainter painter(this);
...
painter.drawPixmap(pos_x, pos_y, width, height, pixmap);
...
Hope that helped!

How to effectively scroll 1024x90000 image in a window?

I have the following UI, where the sonogram (freq+time sound representation) is shown. So the image is not loaded from somewhere, it is drawn by QPainter while reading WAV file.
My current implementation is a single huge QImage object, where the image is drawn. And on paintEvent(), I draw part of the large QImage on the widget:
QPainter painter(this);
// (int, int, QImage*, int, int)
painter.drawImage(0, 0, *m_sonogram, 0, m_offset);
But, as i know, the QPixmap is optimized for displaying pixmaps on the screen, so should I convert the QImage to a QPixmap after the drawing of the sonogram is done?
Also, is it worth to keep large image as some kind of a linked list of separate QPixmap objects of smaller size and make paintEvent() smarter to operate on a list of smaller objects to avoid Qt's auto-cutting procedures and so on?
When my QImage is large enough, each paintEvent() consuming a lot of CPU.
All kinds of advices are welcome :)
Yes, in my limited experience of Qt app development, if you have a static image (or an infrequently updated image) it's well worth (for performance purposes) creating a QPixmap from it and keeping it around to use via QPainter::drawPixmap in your paintEvent handler.
However, I've never tried doing this with anything larger than about 4Kx4K images, so whether it will work for your enormous image or fall over horribly when you start to stress your graphics memory I couldn't say. I'd certainly try it out before considering adding a complicated tiling system.

How to draw a QPoint on a QGraphicsView/Scene

It's really not clear to me how to simply draw a 2d point in QT. I want it to overlay a QPixmap item, but every piece of documentation I find talks about drawing polygons with brushes.
Thanks in advance -
From Qt's documentation:
QImage is designed and optimized for
I/O, and for direct pixel access and
manipulation, while QPixmap is
designed and optimized for showing
images on screen.
So if you have a QPixmap, convert it to QImage and then use QImage::setPixel:
QImage image = pixmap->toImage();
image.setPixel(2, 4, 0x0000ff);
ui->label->setPixmap(QPixmap::fromImage(image)); // show the image in a label

What is the most efficient way to display decoded video frames in Qt?

What is the fastest way to display images to a Qt widget? I have decoded the video using libavformat and libavcodec, so I already have raw RGB or YCbCr 4:2:0 frames. I am currently using a QGraphicsView with a QGraphicsScene object containing a QGraphicsPixmapItem. I am currently getting the frame data into a QPixmap by using the QImage constructor from a memory buffer and converting it to QPixmap using QPixmap::fromImage().
I like the results of this and it seems relatively fast, but I can't help but think that there must be a more efficient way. I've also heard that the QImage to QPixmap conversion is expensive. I have implemented a solution that uses an SDL overlay on a widget, but I'd like to stay with just Qt since I am able to easily capture clicks and other user interaction with the video display using the QGraphicsView.
I am doing any required video scaling or colorspace conversions with libswscale so I would just like to know if anyone has a more efficient way to display the image data after all processing has been performed.
Thanks.
Thanks for the answers, but I finally revisited this problem and came up with a rather simple solution that gives good performance. It involves deriving from QGLWidget and overriding the paintEvent() function. Inside the paintEvent() function, you can call QPainter::drawImage(...) and it will perform the scaling to a specified rectangle for you using hardware if available. So it looks something like this:
class QGLCanvas : public QGLWidget
{
public:
QGLCanvas(QWidget* parent = NULL);
void setImage(const QImage& image);
protected:
void paintEvent(QPaintEvent*);
private:
QImage img;
};
QGLCanvas::QGLCanvas(QWidget* parent)
: QGLWidget(parent)
{
}
void QGLCanvas::setImage(const QImage& image)
{
img = image;
}
void QGLCanvas::paintEvent(QPaintEvent*)
{
QPainter p(this);
//Set the painter to use a smooth scaling algorithm.
p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
p.drawImage(this->rect(), img);
}
With this, I still have to convert the YUV 420P to RGB32, but ffmpeg has a very fast implementation of that conversion in libswscale. The major gains come from two things:
No need for software scaling. Scaling is done on the video card (if available)
Conversion from QImage to QPixmap, which is happening in the QPainter::drawImage() function is performed at the original image resolution as opposed to the upscaled fullscreen resolution.
I was pegging my processor on just the display (decoding was being done in another thread) with my previous method. Now my display thread only uses about 8-9% of a core for fullscreen 1920x1200 30fps playback. I'm sure it could probably get even better if I could send the YUV data straight to the video card, but this is plenty good enough for now.
I have the same problem with gtkmm (gtk+ C++ wrapping). The best solution besides using a SDL overlay was to update directly the image buffer of the widget then ask for a redraw. But I don't know if it is feasible with Qt ...
my 2 cents
Depending on your OpenGL/shading skills you could try to copy the videos frames to a texture, map the texture to a rectangle (or anything else..fun!) and display it in a OpenGL scene. Not the most straight approach, but fast, because you're writing directly into the graphics memory (like SDL). I would also recoomend to use YCbCR only since this format is compressed (color, Y=full Cb,Cr are 1/4 of the frame) so less memory + less copying is needed to display a frame. I'm not using Qts GL directly but indirectly using GL in Qt (vis OSG) and can display about 7-11 full HD (1440 x 1080) videos in realtime.