Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QGraphicsSvgItem crash when rendering into pixmap

QGraphicsSvgItem crash when rendering into pixmap

Scheduled Pinned Locked Moved Solved General and Desktop
14 Posts 5 Posters 959 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Joel BodenmannJ Offline
    Joel BodenmannJ Offline
    Joel Bodenmann
    wrote on last edited by Joel Bodenmann
    #1

    I'm experiencing a crash I don't quite understand when using a QGraphicsSvgItem.

    The application allows the user to select an item from a QTreeView and dragging it into the QGraphicsScene. Quite a while back, I implemented a function to render a QGraphicsItem into a QPixmap outside a QGraphicsScene. The reason for this is simple: When the user drags an item from the item library tree, I want to render the item into a QPixmap and then set that pixmap in QDrag. This way, when the user starts dragging, he already sees the visual representation of the item and it will just "drop" into the scene. This provides a more natural feeling and greatly increases the precision of where the item will be dropped at in the scene.

    To achieve this, I wrote a function to render a QGraphicsItem into a QPixmap quite a few years ago: https://blog.insane.engineer/post/qgraphicsitem_to_qpixmap/
    The function looks like this:

    QPixmap
    Item::toPixmap(QPointF& hotSpot, qreal scale)
    {
        // Retrieve the bounding rect
        QRectF rectF = boundingRect();
        rectF = rectF.united(childrenBoundingRect());
    
        // Adjust the rectangle as the QPixmap doesn't handle negative coordinates
        rectF.setWidth(rectF.width() - rectF.x());
        rectF.setHeight(rectF.height() - rectF.y());
        const QRect& rect = rectF.toRect();
        if (rect.isNull() || !rect.isValid())
            return QPixmap();
    
        // Provide the hot spot
        hotSpot = -rectF.topLeft();
    
        // Create the pixmap
        QPixmap pixmap(rect.size() * scale);
        pixmap.fill(Qt::transparent);
    
        // Render
        QPainter painter(&pixmap);
        painter.setRenderHint(QPainter::Antialiasing, _settings.antialiasing);
        painter.setRenderHint(QPainter::TextAntialiasing, _settings.antialiasing);
        painter.scale(scale, scale);
        painter.translate(hotSpot);
        paint(&painter, nullptr, nullptr);
        for (QGraphicsItem* child : childItems()) {
            if (!child)
                continue;
            
            painter.save();
            painter.translate(child->pos());
            child->paint(&painter, nullptr, nullptr);
            painter.restore();
        }
    
        painter.end();
    
        return pixmap;
    }
    

    The only "strange" part in this function is that we have to render each child manually as we're rendering outside of a QGraphicsScene. This is explained in more detail in the linked blog post.

    So far, this function hasn't failed me. However, now that I added a QGraphicsSvgItem as a child to one of the items, the function crashes the application.
    The crash occurs inside the QGraphicsSvgItem::paint() function.

    I had a look at the QGraphicsSvgItem::paint() implementation (Qt 5.15.5) in hopes of getting a clue on this:

    void QGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                                 QWidget *widget)
    {
    //    Q_UNUSED(option);
        Q_UNUSED(widget);
    
        Q_D(QGraphicsSvgItem);
        if (!d->renderer->isValid())
            return;
    
        if (d->elemId.isEmpty())
            d->renderer->render(painter, d->boundingRect);
        else
            d->renderer->render(painter, d->elemId, d->boundingRect);
    
        if (option->state & QStyle::State_Selected)
            qt_graphicsItem_highlightSelected(this, painter, option);
    }
    

    But nothing stood out. The d->renderer pointer seems to be initialized in the private implementation.

    Does anybody have a clue what is going on here and how I can prevent that crash?
    Rendering the item where I added the QGraphicsSvgItem as a child inside of a QGraphicsScene works just fine.

    Industrial process automation software: https://simulton.com
    Embedded Graphics & GUI library: https://ugfx.io

    JonBJ 1 Reply Last reply
    0
    • Joel BodenmannJ Joel Bodenmann

      I'm experiencing a crash I don't quite understand when using a QGraphicsSvgItem.

      The application allows the user to select an item from a QTreeView and dragging it into the QGraphicsScene. Quite a while back, I implemented a function to render a QGraphicsItem into a QPixmap outside a QGraphicsScene. The reason for this is simple: When the user drags an item from the item library tree, I want to render the item into a QPixmap and then set that pixmap in QDrag. This way, when the user starts dragging, he already sees the visual representation of the item and it will just "drop" into the scene. This provides a more natural feeling and greatly increases the precision of where the item will be dropped at in the scene.

      To achieve this, I wrote a function to render a QGraphicsItem into a QPixmap quite a few years ago: https://blog.insane.engineer/post/qgraphicsitem_to_qpixmap/
      The function looks like this:

      QPixmap
      Item::toPixmap(QPointF& hotSpot, qreal scale)
      {
          // Retrieve the bounding rect
          QRectF rectF = boundingRect();
          rectF = rectF.united(childrenBoundingRect());
      
          // Adjust the rectangle as the QPixmap doesn't handle negative coordinates
          rectF.setWidth(rectF.width() - rectF.x());
          rectF.setHeight(rectF.height() - rectF.y());
          const QRect& rect = rectF.toRect();
          if (rect.isNull() || !rect.isValid())
              return QPixmap();
      
          // Provide the hot spot
          hotSpot = -rectF.topLeft();
      
          // Create the pixmap
          QPixmap pixmap(rect.size() * scale);
          pixmap.fill(Qt::transparent);
      
          // Render
          QPainter painter(&pixmap);
          painter.setRenderHint(QPainter::Antialiasing, _settings.antialiasing);
          painter.setRenderHint(QPainter::TextAntialiasing, _settings.antialiasing);
          painter.scale(scale, scale);
          painter.translate(hotSpot);
          paint(&painter, nullptr, nullptr);
          for (QGraphicsItem* child : childItems()) {
              if (!child)
                  continue;
              
              painter.save();
              painter.translate(child->pos());
              child->paint(&painter, nullptr, nullptr);
              painter.restore();
          }
      
          painter.end();
      
          return pixmap;
      }
      

      The only "strange" part in this function is that we have to render each child manually as we're rendering outside of a QGraphicsScene. This is explained in more detail in the linked blog post.

      So far, this function hasn't failed me. However, now that I added a QGraphicsSvgItem as a child to one of the items, the function crashes the application.
      The crash occurs inside the QGraphicsSvgItem::paint() function.

      I had a look at the QGraphicsSvgItem::paint() implementation (Qt 5.15.5) in hopes of getting a clue on this:

      void QGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                                   QWidget *widget)
      {
      //    Q_UNUSED(option);
          Q_UNUSED(widget);
      
          Q_D(QGraphicsSvgItem);
          if (!d->renderer->isValid())
              return;
      
          if (d->elemId.isEmpty())
              d->renderer->render(painter, d->boundingRect);
          else
              d->renderer->render(painter, d->elemId, d->boundingRect);
      
          if (option->state & QStyle::State_Selected)
              qt_graphicsItem_highlightSelected(this, painter, option);
      }
      

      But nothing stood out. The d->renderer pointer seems to be initialized in the private implementation.

      Does anybody have a clue what is going on here and how I can prevent that crash?
      Rendering the item where I added the QGraphicsSvgItem as a child inside of a QGraphicsScene works just fine.

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #2

      @Joel-Bodenmann
      It would be helpful if you have Qt 5.15 compiled from source and you let it "crash" under the debugger?

      Joel BodenmannJ 1 Reply Last reply
      0
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #3

        Hi,

        How are you creating the new item on the scene ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        0
        • JoeCFDJ Online
          JoeCFDJ Online
          JoeCFD
          wrote on last edited by
          #4

          Even when you find the bug inside Qt, you may not be able to fix it. But you can file a bug or try a newer version of Qt. How about create QGraphicsItem with svg image? Instead of using QGraphicsSvgItem. Whenever the size of image is changed, simply create it from your svg file and the resolution of your image will not be affected. .

          1 Reply Last reply
          0
          • JonBJ JonB

            @Joel-Bodenmann
            It would be helpful if you have Qt 5.15 compiled from source and you let it "crash" under the debugger?

            Joel BodenmannJ Offline
            Joel BodenmannJ Offline
            Joel Bodenmann
            wrote on last edited by
            #5

            @JonB said in QGraphicsSvgItem crash when rendering into pixmap:

            It would be helpful if you have Qt 5.15 compiled from source and you let it "crash" under the debugger?

            That is true. However, that would come with quite a sizable time investment which I'd like to avoid in case this is a "common issue" or something else that someone more knowledgeable than me can point me towards :)

            @SGaist said in QGraphicsSvgItem crash when rendering into pixmap:

            How are you creating the new item on the scene ?

            The QGraphicsSvgItem is simply a member of another QGraphicsObject. Roughly like this:

            class foo :
                public QGraphicsObject
            {
            public:
                void
                set_icon_path(const QString& path)
                {
                    if (m_svg_item)
                        delete m_svg_item;
                
                    m_icon_filepath = filepath;
                    if (m_icon_filepath.isEmpty() || !QFileInfo::exists(filepath))
                        return;
                
                    m_svg_item = new QGraphicsSvgItem(m_icon_filepath, this);
                    m_svg_item->setPos(ICON_PADDING, ICON_PADDING);
                }
            
            private:
                QGraphicsSvgItem* m_svg_item = nullptr; 
                QString m_icon_filepath;   
            };
            

            That's pretty much everything related to the QGraphicsSvgItem in my code base. I simply create the item and set the parent accordingly in the constructor.

            @JoeCFD said in QGraphicsSvgItem crash when rendering into pixmap:

            Even when you find the bug inside Qt, you may not be able to fix it. But you can file a bug or try a newer version of Qt. How about create QGraphicsItem with svg image? Instead of using QGraphicsSvgItem. Whenever the size of image is changed, simply create it from your svg file and the resolution of your image will not be affected. .

            I'm not necessarily expecting a bug in Qt. I started looking at the QGraphicsSvgItem implementation in hopes of figuring out whether I am missing something crucial that I also missed when reading the docs.
            Your workaround of course makes sense but I'd much rather not do that. After all, QGraphicsSvgItem is working well within the QGraphicsScene. My issue is solely related to rendering it outside of a scene.

            Industrial process automation software: https://simulton.com
            Embedded Graphics & GUI library: https://ugfx.io

            1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              One maybe silly question... Why do you need that class at all ? It seems to be a wrapper that reduces the QGraphicsSvgItem API.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              Joel BodenmannJ 1 Reply Last reply
              0
              • SGaistS SGaist

                One maybe silly question... Why do you need that class at all ? It seems to be a wrapper that reduces the QGraphicsSvgItem API.

                Joel BodenmannJ Offline
                Joel BodenmannJ Offline
                Joel Bodenmann
                wrote on last edited by
                #7

                @SGaist said in QGraphicsSvgItem crash when rendering into pixmap:

                One maybe silly question... Why do you need that class at all ? It seems to be a wrapper that reduces the QGraphicsSvgItem API.

                Not a silly question by any means. The foo class above is just an excerpt that shows everything related to the handling of the QGraphicsSvgItem. In the actual application, the class foo first of all isn't named foo and secondly contains a whole lot more code (which is 100% unrelated to the SVG item hence not shown here) :)

                Industrial process automation software: https://simulton.com
                Embedded Graphics & GUI library: https://ugfx.io

                1 Reply Last reply
                0
                • SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Ok, then another comprehension question. Is your goal to update the QDrag pixmap while you are moving over the scene ?

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  Joel BodenmannJ 1 Reply Last reply
                  0
                  • SGaistS SGaist

                    Ok, then another comprehension question. Is your goal to update the QDrag pixmap while you are moving over the scene ?

                    Joel BodenmannJ Offline
                    Joel BodenmannJ Offline
                    Joel Bodenmann
                    wrote on last edited by
                    #9

                    @SGaist said in QGraphicsSvgItem crash when rendering into pixmap:

                    Is your goal to update the QDrag pixmap while you are moving over the scene ?

                    Nope. The QDrag pixmap gets generated when the drag starts (in QTreeView::startDrag()) and never changes afterwards.

                    Industrial process automation software: https://simulton.com
                    Embedded Graphics & GUI library: https://ugfx.io

                    1 Reply Last reply
                    0
                    • SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      In that case, why not simply load the file in a QPixmap ? The QtSvg module provides an image format plugin so there's no need for that item.

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      1 Reply Last reply
                      0
                      • Joel BodenmannJ Offline
                        Joel BodenmannJ Offline
                        Joel Bodenmann
                        wrote on last edited by Joel Bodenmann
                        #11

                        Looking at these questions I think it is reasonable that I provide some background information not given in the original post as I deemed it "out of scope" - so where we go :)

                        I have an application using the Qt Graphics Framework. In that application, a user can create a flowchart-like diagram. Here's a screenshot of the application:
                        e9126be6-cf00-4aa1-a53e-00655d4d7dec-image.png
                        This is done using the open-source QSchematic library (disclaimer: I'm the author).

                        In the screenshot above, the blocks with the blue header and the white body is based on the QSchematic::Node class (which itself inherits from QGraphicsObject). Each node can have zero or more connectors. A connector (QSchematic::Connector) inherits from QGraphicsItem. These connectors are children of the QSchematic::Node. Nothing special here.

                        Those nodes can also have an icon. In the screenshot above, only the top-left node shows an icon. As of now, I simply keep a QIcon inside my node class and paint that manually in the node's paint() override. The problem here is that when the user zooms in, the icons don't look pretty. The icons themselves (the resources/assets) are SVGs but as I load them into a QIcon they get rasterized and end up looking unpleasant when zooming into the scene.
                        To solve this issue, I decided to add another child item to the node class - the aforementioned QGraphicsSvgItem.

                        As mentioned in the opening post the QGraphicsSvgItem works just fine inside the scene. The part where my application crashes is when I render a preview of the node using the toPixmap() function shown in my opening post.
                        The reason why I am using this toPixmap() function is to render the node before it actually becomes part of the scene. There is an item library built around a QTreeView from which the user can drag an node and drop it onto the scene/view. To make things look nice and to provide the user with a visual reference of where the block will be placed, I render the block into a QPixmap and assign that to the QDrag object. Here is a video of that in action: https://www.veed.io/view/4d2b2da8-3016-4b26-b55f-ea27db793f98

                        The summary of this is still: I have a custom graphics item composed of several child items, everything works fine inside the scene but when I render it outside the scene using the toPixmap() function shown in the opening post, the application crashes once QGraphicsSvgItem::paint() is called. The crash is caused by a seg fault.

                        @SGaist said in QGraphicsSvgItem crash when rendering into pixmap:

                        In that case, why not simply load the file in a QPixmap ? The QtSvg module provides an image format plugin so there's no need for that item.

                        I wasn't aware that QPixmap can gain SVG support via QtSVG - that's good intel, thank you!
                        However, would this still raster the image? Or can I "assign" an SVG image to a QPixmap, render the QPixmap in a QGraphicsItem and it won't look "pixelated" when the user zooms in?
                        The reason why I choice QGraphicsSvgItem is two fold: I didn't know that QPixmap can receive SVG support and it just handles all the scaling and stuff automatically.

                        Industrial process automation software: https://simulton.com
                        Embedded Graphics & GUI library: https://ugfx.io

                        1 Reply Last reply
                        0
                        • SGaistS Offline
                          SGaistS Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on last edited by
                          #12

                          To show the SVG on the scene, the item is the right tool.
                          I would simply use the pixmap for the drag image rendering. In case the result is not what you expect, there's also the QSvgRenderer class that might be of interest.

                          Interested in AI ? www.idiap.ch
                          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                          1 Reply Last reply
                          0
                          • Joel BodenmannJ Offline
                            Joel BodenmannJ Offline
                            Joel Bodenmann
                            wrote on last edited by Joel Bodenmann
                            #13

                            It has been a while but I'm an advocate for posting the solution even if a long time has passed...

                            The problem was that in QSchematic::Items::Item::toPixmap() (snipped shown in the opening post here) where I render the QGraphicsItem into a QPixmap I passed nullptr for the QStyleOptionGraphicsItem parameter.
                            When I create a default-constructed QStyleOptionGraphicsItem and pass a pointer to that instead everything works as expected.

                            I assume (!) that QGraphicsSvgItem requires QStyleOptionGraphicsItem to be non-null.

                            QSchematic upstream commits fixing this:

                            • https://github.com/simulton/QSchematic/commit/184f1428e4931fab857633e398976af572aadc3d
                            • https://github.com/simulton/QSchematic/commit/10452a67ac035471039d5416551be2ff98a0bcb2

                            Industrial process automation software: https://simulton.com
                            Embedded Graphics & GUI library: https://ugfx.io

                            jeremy_kJ 1 Reply Last reply
                            1
                            • Joel BodenmannJ Joel Bodenmann has marked this topic as solved on
                            • Joel BodenmannJ Joel Bodenmann

                              It has been a while but I'm an advocate for posting the solution even if a long time has passed...

                              The problem was that in QSchematic::Items::Item::toPixmap() (snipped shown in the opening post here) where I render the QGraphicsItem into a QPixmap I passed nullptr for the QStyleOptionGraphicsItem parameter.
                              When I create a default-constructed QStyleOptionGraphicsItem and pass a pointer to that instead everything works as expected.

                              I assume (!) that QGraphicsSvgItem requires QStyleOptionGraphicsItem to be non-null.

                              QSchematic upstream commits fixing this:

                              • https://github.com/simulton/QSchematic/commit/184f1428e4931fab857633e398976af572aadc3d
                              • https://github.com/simulton/QSchematic/commit/10452a67ac035471039d5416551be2ff98a0bcb2
                              jeremy_kJ Offline
                              jeremy_kJ Offline
                              jeremy_k
                              wrote on last edited by
                              #14

                              @Joel-Bodenmann said in QGraphicsSvgItem crash when rendering into pixmap:

                              I assume (!) that QGraphicsSvgItem requires QStyleOptionGraphicsItem to be non-null.

                              That is correct. Beyond that, the documentation doesn't suggest that anything other than a valid object is expected.

                              Asking a question about code? http://eel.is/iso-c++/testcase/

                              1 Reply Last reply
                              2

                              • Login

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved