SVG support in CEGUI
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 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)
- The SVG File loading and parsing will be part of CEGUI Core-library (undecided)
Classes and structures:
- SVGImage will be a subclass of CEGUI::Image (yes)
- SVGImage contains the cached information required for rendering (yes)
- 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 (undecided)
- 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. (undecided)
- 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. (undecided)
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) (undecided)
- 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.
- When defining an Image in the imageset, as an alternative to a relative definition in respect to the Imageset's SVG file, each Image should also be able to reference a specific SVG file directly which would be then used for this image as a whole in the process of parsing the Imageset. A pos/size definition should in that case not be possible because that a seperate imageset should be created for. (undecided)
Notes: Probably SVGImage should rather be called VGImage or VectorImage or VectorGraphicsImage. This is because it should be easily possible to make that the class would not require actual SVG data but instead only require shape information for drawing. The SVG data can be converted into such abstract shape information. That way users can create their own shape information and which can be used in a VGImage. None of the names should refer to "SVG" as a name in my opinion, except for the SVG parser itself. Everything after the parser should be "general" although the whole data structures and drawing process will be designed in the first line to go well along with the SVG file standard and its definition of vector graphics.
We should probably think of a general naming concept, maybe using a common prefix for all files such as "VG-", and also maybe seperate all Vector Graphics related files into its own src/includes folder such as done with "widgets", unless this is not desired.
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.