Qpainter scale pixmap to parent with aspect ratio



  • Hi there,

    I have a mainwindow (red). Inside there I have a QFrame derived "VideoFrame" (green).
    Inside that I have an overwritten paintevent and a QPainter. Inside that I have a centered painter.pixmap().

    0_1529305105666_Bildschirmfoto_2018-06-18_08-37-26.png

    I want that painter/pixmap to grow to the edges of the green Frame if I resize the mainwindow, but keeping its aspect ratio (should be 1.333333 with 320x240).

    I simply dont get behind how to do it but I tried it with painter.scale():

    void VideoFrame::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform,1);
        QPixmap pxmap;
        QPoint center(this->width()/2,this->height()/2);
        painter.translate(center);
    
        painter.scale(1*(this->width()/240.0),1*(this->width()/320.0));
    
        painter.rotate(v_rotation);
        painter.translate(0-320/2,0-240/2);
        painter.drawPixmap(0,0,320,240,QPixmap::fromImage(QImage("image_320.png")));
    
    }
    

    The problem with that is it grows over the top/buttom edges, but stays inside left/right and keeps aspect ratio, so I guess I am close to the solution...

    0_1529305412522_Peek 2018-06-18 09-02.gif

    Any ideas anyone?


  • Moderators

    @pauledd
    i suggest to cache the pixmap upon widget resize and just paint it in the paintEvent().

    void VideoFrame::resizeEvent(QResizeEvent* event)
    {
         const QSize size = event->size();
         m_CachedPixmap =  size.height() < size.width() ? m_Pixmap.scaledToHeight(size.height(), Qt::SmoothTransformation) : m_Pixmap.scaledToWidth(size.width(), Qt::SmoothTransformation);
         this->update();
         QWidget::resizeEvent(event);
    }
    
    void VideoFrame::paintEvent(QPaintEvent *)
    {
       ...
       const QSize pixSIze = m_CachedPixmap.size();
       const int x = (this->rect().width() - pixSize .width()) / 2.0;
       const int y = (this->rect().height() - pixSize .height()) / 2.0;
       painter.drawPixmap(x,y, pixSize.width(), pixSIze .height(), m_CachedPixmap);
       ...
    }
    

    (untested)



  • void VideoFrame::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        QSize widgetSize = rect().size();
        const QPixmap paintPixmap(QStringLiteral("image_320.png"));
        const auto newHeight = widgetSize.width()*paintPixmap.height()/paintPixmap.width();
        if(newHeight<=widgetSize.height())
            widgetSize.setHeight(newHeight);
        else
            widgetSize.setWidth(widgetSize.height()*paintPixmap.width()/paintPixmap.height());
        style()->drawItemPixmap(&painter,rect(),Qt::AlignCenter,paintPixmap.scaled(widgetSize));
    }
    


  • thanks to you both :)
    @raven-worx
    I tried your code but the image "plopps" strangely and gets bigger than the frame for a short time...

    0_1529331170841_Peek 2018-06-18 16-10.gif
    Why caching the pixmap? Did you do this for performance reasons?

    @VRonin
    Your code worked flawlessly.

    I will later replace the image file again with a pointer to an QImage that gets updated at 9fps (thermal camera source) so @VRonin solution "feels" better for now, because I dont know how this caching the image while resize will impact my livestream...


  • Moderators

    @pauledd said in Qpainter scale pixmap to parent with aspect ratio:

    I tried your code but the image "plopps" strangely and gets bigger than the frame for a short time...

    as stated its untested, and straight out of my head. But i guess i'ts just a minor mistake in the calculation.

    Why caching the pixmap? Did you do this for performance reasons?

    yes, because there is no need to load a pixmap from file in every paintEvent() call!



  • @raven-worx said in Qpainter scale pixmap to parent with aspect ratio:

    yes, because there is no need to load a pixmap from file in every paintEvent() call!

    Okay but later I will use this:

    bool VideoFrame::startCam()
    {
    ...
    for(int i=0;i<img->height();i++)
        {
            memcpy(img->scanLine(i), frame1.row(i).data, img->bytesPerLine());
        }
        update();
    ...
    }
    ...
    
    
    
    void VideoFrame::paintEvent(QPaintEvent *)
    {
    ...
        const QPixmap paintPixmap(QPixmap::fromImage(*img));
    ...
    }
    

    I have camera data from frame1 that is constantly copied to "img" and then drawed in the paintEvent... would that caching make then sense too? I used the static image just as placeholder to demonstrate...

    @VRonin
    I got a new problem with your code.. how can I then rotate the rect/image around its centerpoint. If I simply rotate the painter the centerpoint lays in the upper left corner. I then tried to translate the painter to half of the image dimensions and then tried painter.rotate but with your code that doesnt work anymore, can you give me a hint how to solve that?


  • Moderators

    @pauledd said in Qpainter scale pixmap to parent with aspect ratio:

    I have camera data from frame1 that is constantly copied to "img" and then drawed in the paintEvent... would that caching make then sense too?

    basically yes, since you do not know how often the paintEvent() will be called until the next image is received.

    And also btw, it's not necessary to convert the QImage to a QPixmap just for drawing. QPainter also provides methods to draw a QImage directly.



  • @pauledd said in Qpainter scale pixmap to parent with aspect ratio:

    how can I then rotate the rect/image around its centerpoint.

    Do not rotate the painter, rotate the pixmap.

    QPainter painter(this);
        QSize widgetSize = rect().size();
        QPixmap paintPixmap(QStringLiteral("image_320.png"));
        QTransform rotateTransform;
        rotateTransform.rotate(45);
        paintPixmap=paintPixmap.transformed(rotateTransform);
        const auto newHeight = widgetSize.width()*paintPixmap.height()/paintPixmap.width();
        if(newHeight<=widgetSize.height())
            widgetSize.setHeight(newHeight);
        else
            widgetSize.setWidth(widgetSize.height()*paintPixmap.width()/paintPixmap.height());
        style()->drawItemPixmap(&painter,rect(),Qt::AlignCenter,paintPixmap.scaled(widgetSize));
    

    Caching does makes a lot of sense, you can use QPixmapCache

    @raven-worx said in Qpainter scale pixmap to parent with aspect ratio:

    QPainter also provides methods to draw a QImage directly.

    Yes but:



  • Great, thank you both for the hints, I will try to go on by myself but keep that topic unresolved for a few days as long as I am not finshed.



  • @VRonin I dont yet get behind the way you draw the pixmap in your code.
    you use (still noob beginner ;) ):

    style()->drawItemPixmap(&painter,rect(),Qt::AlignCenter,paintPixmap.scaled(widgetSize));
    

    I've never seen such a style()-> ... alone without an object... it seems to be not declared somewhere, to what does it refer to? To the Qpainter?

    The same is with the:

    QSize widgetSize = rect().size();
    

    rect().size(); refers to the QFrame? I usually would expext an "this.rect().size();"

    And that brings me to another question. If I need to draw an ellipse, as I did
    previously with painter.drawEllipse, into that image area, for example at position 40,40 seen from top/left and staying at that point while rotating with the image how would that fit into your way to draw with:

    style()->drawItem...
    

    or is that not possible?


  • Qt Champions 2017

    Hi
    style()
    refers to the QWidget member method that returns the active
    QStyle.
    It would be the same as this->Style().
    Same with
    rect().size();
    also means this-> rect().size();
    They are defined in the base class. ( QWidget )



  • @pauledd said in Qpainter scale pixmap to parent with aspect ratio:

    I usually would expext an "this.rect().size();"

    Do you come from python? it is this->rect() and this->style() but in C++ you don't need to explicitly use this in front.

    to what does it refer to? To the Qpainter?

    http://doc.qt.io/qt-5/qwidget.html#style

    @pauledd said in Qpainter scale pixmap to parent with aspect ratio:

    If I need to draw an ellipse, as I did

    QStyle/QStylePainter is an abstraction over the drawing of controls, it does not provide drawing of basics like lines and circles. See http://doc.qt.io/qt-5/qstyle.html#developing-style-aware-custom-widgets



  • Okay thanks, so I will paint an ellipse as png and then load it the same way you did with the "style()->drawItem..." :)



  • @pauledd said in Qpainter scale pixmap to parent with aspect ratio:

    I will paint an ellipse as png

    Saving and loading to png file format is expensive, I would advise against it.

    If the position of the ellipsis doesn't change in the pixmap then draw it using QPainter on the pixmap and cache it. If the ellipsis is dynamic then paint it directly on the widget from the paintEvent



  • Ok, I will try that...
    Maybe I should have been more clearly what I intend to get...
    I had it all working already, just without the scaling issue

    alt text

    I get camera images and I let opencv compute min/max values and a centerpoint. Somethimes the camera is rotated in a undesired position so the user should be able to rotate the view, including the measurepoints, and more complex including the point values so that the stay at the points but do not rotate staying readable...

    I keep trying with qpainter draw



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.