Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Prevent Icon from Resizing to fit setIconSize



  • I have a toolbar that takes 20x20 icons from buttons, but it can sometimes have actions that contain icons that are 16x16, unfortunately my 16x16 icons rescale up to 20x20 causing them to be soft looking. How can I prevent them from resizing? All my icons are SVG.

    All my icons are made using a custom method, so I can target 16x16 specifically if I need to.

    // dpr = device pixel ratio
    QSize iconSize(16 * dpr, 16 * dpr);
    QPixmap pm = QIcon(str).pixmap(iconSize);
    // then do something to the pixmap here
    QIcon icon;
    icon.addPixmap(pm);
    return icon;
    

    I thought of compositing the icon onto a 20x20 QRect, and although this works fine on Windows, on Ubuntu the icons look like they're scaled up 2x and cropped when on Hdpi (200%) which is more like 400% for the icon, showing only a portion of the icon, but the icon is otherwise high-quality looking.

    // rough code
    QPixmap composite(QPixmap pixmap, int iconWidth, int iconHeight, int offset) {
        // dpr = device pixel ratio
        QPixmap canvas(20 * dpr, 20 * dpr);
        canvas.fill(Qt::transparent
        QPixmap combined(20 * dpr, 20 * dpr);
        combined.fill(Qt::transparent);
    
            if (!pixmap.isNull()) {
                QPainter painter;
                painter.begin(&combined);
                QRect canvasRect(0, 0, canvasWidth, canvasHeight);
                painter.drawPixmap(canvasRect, canvas);
                QRect iconRect(offset, offset, iconWidth, iconHeight);
                painter.drawPixmap(iconRect, pixmap);
                painter.end();
            }
    
        return combined;
    }
    

    Is there an easier way? Or something wrong with the way I'm doing it?


  • Lifetime Qt Champion

    When you calc with dpr you also should set it to the QPixmap: https://doc.qt.io/qt-5/qpixmap.html#setDevicePixelRatio

    But I don't understand: that are 16x16, unfortunately my 16x16 icons rescale up to 20x20 <-> All my icons are SVG.



  • @Christian-Ehrlicher Thanks I'll give that a try.

    For the 16x16 thing, if I setIconSize as 20x20 to the toolbar, but let's say one action has a 16x16 icon (because I reused one from the main QMenu action) the icon is scaled up to fit 20x20. I'd like it to stay 16x16 for this action to keep the icon looking crisp, so I thought of loading two pixmaps into the icon for the purpose, the original 16x16 and another 20x20 composite. But kinda hoped there was a simpler way.


  • Lifetime Qt Champion

    But you said you're using svgs - so why not using them directly so they're properly scaled.



  • @Christian-Ehrlicher Not sure I understand. If I do this and load the SVG directly:

    toolbar->setIconSize(QSize(20, 20));
    action->setIcon(str);
    

    If the SVG is designed for 16x16, it still scales up to fit 20x20. This makes it look too big in the UI and slightly out of focus. I also need to change color of the icon and opacity, that's why I take the pixmap from the QIcon and load it back.


  • Lifetime Qt Champion

    Don't know why svgs are adjusted to a fixed size but let's accept this.
    Then use the function I linked to in my first post to fix the devicePixelRatio of the pixmap.



  • @Kite-R said in Prevent Icon from Resizing to fit setIconSize:

    If the SVG is designed for 16x16, it still scales up to fit 20x20

    SVGs are designed for no specific size. They are vector graphics. They always look good, because the internal vectors can be scaled like you want.

    What @Christian-Ehrlicher wanted to say is, why you cant just give your SVG 20x20 size?



  • @Pl45m4 said in Prevent Icon from Resizing to fit setIconSize:

    SVGs are designed for no specific size. They are vector graphics. They always look good, because the internal vectors can be scaled like you want.

    Yes, I tried QSvgRenderer before, but I can't get it to work at Hdpi. Perhaps someone could help me with this, I made a super simple function to paint the SVG to a QImage for this example, and this works except at Hdpi. I have Hdpi pixmaps enabled etc. in main.

    #include <QScreen>
    #include <QSvgRenderer>
    #include <QPixmap>
    #include <QImage>
    #include <QPainter>
    #include <QIcon>
    #include <QString>
    #include <QDebug>
    
    QIcon customIcon(const char *iconName) {
        QScreen *screen = QApplication::primaryScreen();
        const qreal dpr = screen->devicePixelRatio();
    
        QString str = QString(":/icons/") + iconSVGName + ".svg";
    
        QSvgRenderer renderer(str);
        QImage img(16, 16, QImage::Format_ARGB32);
        QPainter painter(&img);
        img.setDevicePixelRatio(dpr);
        renderer.render(&painter);
        qDebug() << img;
    
        QIcon icon;
        icon.addPixmap(QPixmap::fromImage(img));
    
        return icon;
    }
    

    This is the result at Hdpi (dpr = 2), it's cropped and fuzzy, it's supposed to be a triangle:
    HdpiButton3.png

    How it looks if I remove setDevicePixelRatio from the image, a good size, but blurry:
    HdpiButton2.png


Log in to reply