SVG support in CEGUI

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search

The purpose of this document is to determine how the SVG loading/handling and storage of related datastructures should be done.

Decisions that have to be made

Specific questions that need to be decided upon by the CEGUI team are: (in the brackets the current state of decision is noted)

Module-integration:

  • SVGImage and related files will be a part of CEGUI Core-library (yes)
  • Vector graphics rendering related files will be part of CEGUI Core-library (yes, they are in the subfolder "svg")
  • The SVG File loading and parsing will be part of CEGUI Core-library (yes, it is implemented in SVGData)

Classes and structures:

  • SVGImage will be a subclass of CEGUI::Image (yes)
  • SVGImage contains the cached information required for rendering (yes, it is cached inside GeometryBuffers)
  • A class called VGShapeData, VectorShapeData, VectorData or similar, will contain the abstract information on the shapes which is used in SVGImage to be cached and drawn (yes, implemented as SVGData with similar behaviour)
  • SVGImage will contain a reference to a shape-data class. This means that multiple SVGImages can share the same shape data. The question is if this is necessary (use-case?). If this data was shared and the user modified this data, all VGImages would have to be updated. An alternative would be that the shape-data class can only be referenced once, but can be copied and then altered if required. (yes, SVGImage contains a reference to a possibly SVGData object that can be used by multiple (or none) SVGImages)
  • CEGUI::VectorShape (or similar) will be a superclass for all draw shapes. These can be constructed manually but will also be used directly by the SVG parser. The mentioned ShapeData class will store the various shapes which can then be used for creating actual geometry in SVGImage. (SVGData contains the different shapes. SVGBasicShape is the superclass for all Basic Shapes defined by SVG)

SVG loading and parsing:

  • An SVG file can be referenced from within an Imageset file and used just in the same way as texture atlases are used (Pos+Size define an Image in it) (yes, already implemented that way)
  • An Image defined in an SVG-Imageset can also address a group/layer using an optional "Group" or "Layer" attribute. This means that only data from the specified group will be used for the Image. (not yet implemented)
  • When defining an Image in the Imageset an alternative to defining an area inside the Imageset's SVG file an Image should also be able to be referenced directly as a seperate SVG file directly. In that case the Image's area will span over the whole SVG image. A pos/size definition should in that case not be valid in XML because for that a seperate imageset should be created instead. (not yet implemented)

Notes: The new classes will be, as much as possible based, on the SVG standard and their names will carry SVG as prefix.

Parsing

  • Should a warning be thrown if the svg file's version does not match SVG Tiny 1.2 (or lower) version? As we won't fully support all features of any profile either way, this might be redundant and it should just be documented in the CEGUI docs and SVG class docs what is supported and what isn't. (undecided)

Possible use-cases for SVG Images

As justification for the above suggestions following use-cases are to be considered:

  • A user wants to load an SVG-file and then use the resulting SVGImage like a regular raster graphics image (BasicImage)

For this purpose, in the optimal case, the user could use an SVGImage equally to a BasicImage. Which means, it can be referred to via a String, defining the name of the Image. The VGImage would therefore have to be registered and maintained by the ImageManager in the same way as raster graphics. Autoscale and scaling in general, as well as any clipping have to be handled appropriately for that case. Stretching and alignment also have to be considered.

  • A user wants to load an SVG-file with a progressbar. The progressbar image consists of overlapping parts as it would look in the final version. The image parts are seperated into different layers and the user wants most of the Image definitions to be using the data of only one specified layer

Images will contain an [optional] extra attribute "Group" or "Layer" which allow specifying a layer or group. The SVG group's contents will then exclusively be taken as shape information for the SVGImage.

  • A user wants to create a drawable Vector graphics image, e.g. a mouse cursor, by defining it manually using CEGUI classes

The class maintaining the shape information has to store the draw-information the user wants to generate. An instance of this class has to be created using a CEGUI Manager. When it is constructed it should be possible to call functions that write the draw commands to this file. Such a command could be for example: addDrawLine(Point start, Point end, thickness, Colour col, ...) The question is if this function should remain inside the class storing the data, or if a Helper or super-class should deal with the interpretation of the draw command. Probably it is better to seperate it in some way from the data storage class (VectoData) to prevent it from containing too many function that are not of direct relevance. Alternatively, each draw shape could be a seperate class. This might be the best solution as the data structure would only need an add(const Shape& shape) function and therefore the rest would be duty of the specific implementation of the Shape. The Shapes would be passed as copy to the the data storage class so that the user doesnt have to take care of its memory management later-on. The data storage class would be managed by a Manager, similar to CEGUI::Texture and therefore could be addressed via a String and would be deleted and created analogously.

  • (Optional/for post-GsoC) Usage of Animated Vector Graphics

Subclass of SVGImage, containing a time variable as addition. Needs to be updated on each update call that includes a time-change. This also requires recaching. Currently I do not see further issues with that, the main problem with these is the recaching process itself.


Currently I cannot think of further use-cases.

SVG parsing / Imagesets

Summary of my research: According to my research it would be best to specify images via an imageset file analogous to how it is done with raster graphics. They can then reference positions and dimensions in an SVG file to seperate the images. In case the user wants to use different layers as different images, the Image specification in the Imageset will also provide an attribute for Group(=Layer in Illustrator/Inkscape), to identify the specific group from which the vector shapes should exclusively be taken from. The default Image dimensions will be equal to the imageset dimensions and the position will be (0,0) in case these were not specified in the Image attributes.


The long version:

I have not yet checked how it is in Inkscape but Adobe Illustrator at least does not support a way to easily create multiple vector graphic images in a single file, that could then be loaded with a clear distinction from each other. But the SVG file standard itself also does not really offer a solution for this as I will mention in the following text. When i talk about SVG i always refer to the SVG 1.1 standard:

What could in general be done to create multiple SVG Images in Adobe Illustrator, is to make multiple layers, which then would each get converted to groups when saving to an SVG file, where each group can then be interpreted as one image. However, these groups would all be using the same workspace area, which is clearly defined in Illustrator and also specified by the SVG format. So this workspace area is a clear definition of boundaries which would be logical to use as boundaries of the image itself. There is no real other way to know the dimensions of a desired image, except maybe check the minimum and maximum dimensions, but this could lead to undesired results, such as too closely cropped images without margin. For a more intuitive approach, which is probably also more logical to users, the area defined in the SVG header ( http://www.w3.org/TR/SVG/struct.html#SVGElement ) should be used to define the dimensions of the image. This are is specified in Illustrator via the Artboard, whereas only the Artboard 1 seems to be exported when there are multiple ones. One other way this could have theoretically be done is by using groups, but they simply do not provide such attributes as defined in SVG 1.1: http://www.w3.org/TR/SVG/struct.html#Groups . So the only thing the user can do is to have all images scaled to suit the same workspace area, whereas each layer is one image. Obviously the problem here is that such odd scaling is not a way to work with. A perfect solutin would be to simply export the desired collection of vector graphics images and use an imageset which is saved seperately, this would work in the same way as it is working with the raster graphic texture atlases and their imagesets right now. But this would require some, hopefully only small, changes to the Imageset Editor of CEED, to make CEED compatible with this. Unfortunately i am pretty sure that I do not have enough knowledge of python and CEED to apply such changes myself, as I would then most definitely exceed the timeframe for this gsoc project. Still, the Imageset creation can be done manually in XML. As addition it has to be considered that the dimensions and positions in an SVG file are not necessarily specified in pixels.

Although not considerably different, I will in any case refer to the SVG Tiny 1.2 standard instead of SVG 1.1, as it is newer and "smaller" : http://www.w3.org/TR/SVGTiny12


SVG format support

In this section I will record what elements, attributes and features of SVG will be supported by my parsing and rendering classes and how special cases will be handled.

The first line of SVG files will usually contain the xml tag such as: ?<?xml version="1.0" encoding="UTF-8"?> For the xml meta-data probably no action has to be taken in any case. After this line, some programs (such as Adobe Illustrator) might add a comment line containing info about the used program and used exporter. This, we can also ignore.

The following element is the <svg> element, which encompasses all the data contained in the SVG file. "An SVG document fragment can only contain one single 'svg' element, this means that 'svg' elements cannot appear in the middle of SVG content. " We will only parse this first svg element and ignore all further elements if there are any. For this element there are several attributes. Version and Baseprofile could be relevant, as they would allow us to throw a warning in case they do not match SVG Tiny 1.2 or lower. The question is if this is helpful - see questions on top. Other attributes of the svg element that could be relevant are: width, height and viewBox. The viewBox offers a scalable solution, which describes the region of world coordinate space (the initial user coordinate system) used by the graphic. It requires a list of 4 numbers (no units) seperated with a space. Width and height provide a fixed size for the SVG image, so that it will not scale for different display resolutions. "These two attributes define the intrinsic aspect ratio and (unless both width and height are percentages) the intrinsic size of the svg element. " As viewBox would be ambiguous for usage in CEGUI (we already have attributes for stretching and orientation in CEGUI), as well as the usage of any real-world units (inches, cm, etc.) and % for width and height would be ambiguous (what pixel-size to apply to a left-oriented percentual image? should it be based on the host window size, or ImageryComponent area, etc? - or how many pixels is an "inch" on a users screen?) we will, for now, ONLY accept width and height attributes with pixel units for this and throw a warning if they are not present, taking the width and height values as pixel values instead. Further issues would arise when defining SVG sub-images inside the an Imageset (such as was suggested before). The question that would arise ther eis how the sub-images would be defined in relation to percentual SVGImage sizes or if using a viewbox definition. Because of the mentioned reasons I decided it is best to force the user to offer an SVG file that is defined using absolute pixel dimensions and is as such ready to be rendered without ambiguosness regarding Imagesets or effective rendering dimensions in screen space. For the case of users who create the SVGData object on their own, the size will by default be the maximum values of width and height (therefore the unsigned value will be set to -1).

All SVG Basic Shapes will be supported fully: 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon'.

Animation will not be supported so far.

Fonts in SVG images are not planned to be supported in CEGUI at any point of time. If users want to use a Font in their SVG graphics they can simply vectorize the Font and export it as such, which of course leads to a loss of scalability. Real support for this feature is impossible considering the way CEGUI renders fonts at the moment.

'transform' will only support a matrix value as of now, because that is the only thing illustrator seems to actually export. The definition of transforms can be found here: http://www.w3.org/TR/SVGTiny12/coords.html#TransformList

Custom drawing helper functions

To make it easier to draw custom geometry, we could extend the SVGData class with wrapper functions that contain necessary calls to draw basic elements. This would work in similar ways as with HTML5 canvas: http://www.w3schools.com/html/html5_canvas.asp or not.

References

A couple of websites i used as inspiration, reference or as a loose basis for my own code:

SVG Tiny 1.2 Standard: http://www.w3.org/TR/SVGTiny12/

Stroke drawing: http://www.codeproject.com/Articles/226569/Drawing-polylines-by-tessellation

Fast circle and arc drawing: http://slabode.exofire.net/circle_draw.shtml

Tesselated AA: http://www.codeproject.com/Articles/199525/Drawing-nearly-perfect-2D-line-segments-in-OpenGL