Thursday, August 03, 2006

SVG on graphicsview

A lot of people has been asking for a simple way of rendering SVG files on top of qgraphicsview. The only catch was that most of them wanted it along a way of doing selective rendering of SVG files. For example people create huge SVG files and use them as collection of items. Individual elements are nested inside a group element that has a predefined xml:id. I've spent most of yesterday just fixing things in QtSvg to make it possible. The thing that makes it tricky is the following: lets say that we have a SVG fragment with the following structure:
<g transform="scale(0.5,0.5)">
  <g id="myElem" transform="scale(2,2)">
    <rect x="0" y="0" width="2" height="2"       transform="translate(20,20)" />
  </g>
</g>
So we have a group that defines a transformation matrix and inside that group we have our predefined element with id="myElem". The question is what is the bounding box of our rectangle and how do we make sure it fills out the complete area of the rendering viewport. To do that we need to find the rendered element and traverse starting from that element down through its children propagating the transformation matrix correctly. So when we're in rect out transformation matrix is defined by the rect transformation (one translating by 20 in each direction) matrix muliplied by the parent transformation matrix (scaling by 2 in each direction). So the actual bounding box of the rectangle is x=40, y=40, width=4, height=4. Having that all we need to do is set our window to those dimensions and render the element. The code to traverse the SVG tree and QGraphicsSvgItem are already in Qt so once we'll update the snapshots you'll have it.

One of the things that are possible with it is for example having an entire card deck in an SVG file and putting individual cards into predefined groups. I wrote a quick example that takes such constructed SVG files, fetches the cards from the SVG and renders them with some arbitrary transformations on QGraphicsView. A pretty nice feature of it is that I made sure the SVG file has to be loaded only once. QGraphicsSvgItem's can share the context and are capable of picking the right parts of the SVG that actually constitute their whole visual appearance. Thanks to that no unecessary parsing or processing of the SVG tree takes place. A screenshot follows :)

So now it's possible to use QGraphicsView along SVG elements. And since QGraphicsItem's handle all input events one can do pretty nice things by using SVG to define look of the items and handle the logic in the code.

8 comments:

JImmy said...

Zack: hope you don't mind a completely unrelated comment, but I wondered if you might be interested in the rambled ideas of an OpenGL coder. I don't know QT all that well, other than as an end user of QT apps.

Scroll panes in qt seems not to be not hardware accelerated, which to my confused mind is missing a trick.

Whenever I need to code a scroll for an OpenGL app/game, I create a texture of the whole area being scrolled over, and then just move a surface's texture co-ords arround to create a hardware-accelerated scrolling effect.

The disadvantage of this is more of the area to be scrolled over has to be rendered at start, but so long as the area isn't massive it generally makes things an order of magnitude faster.

Silky 'smooth scrolling' should be really fast this way (faster than the monitor can draw I'd have thought) but use hardly any CPU time.

This also allows some groovy effects, for example the scroll pane can look... well, like a scroll! So that at the edges, the content is compressed to give the effect of 'wrapping round' (maybe a screenshot would be easier to describe this).

Daniel "Suslik" D. said...

Enabling "selective" SVG rendering is the single thing that suddenly solves "Bad scaling of SVG icons" issue. An artist would just include a low detail "lo-rez" and high detail "hi-rez" layers. No need to play with pasters any more...

superstoned said...

@ JImmy: sounds cool, esp the scroll-looking thing! I hope zack reads comments on his blog :D

Anonymous said...

Do you know Amanith?

Zack said...

@jimmy: the problem would be then that widgets inside scrollpane behave differently than other widgets because they are suddenly forced to render to an offscreen area that is available to the parent widget. Also note that widgets inside the scrollarea can be huge, and the texture size limitations would make it rather tricky to get right. Having said that for Qt 4.3 I'm planning to implement something along those lines (in Qt 4.2 we already have QWindowSurface class which is basically what is needed to implement this, it's just that for 4.2 we haven't done OpenGL implementation of it :) ).

@anonymous: yes, i talked to the guys behind it when they started it. Qt does something very similar in qpaintengine_opengl. Why do you ask?

Anonymous said...

I want a card deck screensaver :)

Anonymous said...

In the Amanith forum there is a good explanation of how their tassellator works. The Amanith tassellator is very fast and good (IMHO).

Zack said...

To be honest in Qt we have two tessellation algorithms both a lot better. But this blog wasn't about tessellation which for OGL we're moving away from due to the method I described a few blogs back =) On x11 we'll use my new tessellator.