Unifying The Rendering Interfaces

page to keep track of ideas and notes concerning "pluggable" renderers, and their interface with the main mapserver program


Adding new renderers, and maintaining the current ones is getting quite complex due to functional code duplication inside each renderer.

Current way of doing things

int msDrawXXX(image, shape, symbol, scalefactor ...) {
  if renderGD
    return msDrawXXXGD(image, shape, symbol, scalefactor ...);
  else if renderAGG
    return msDrawXXXAGG(image, shape, symbol, scalefactor ...);
  else if renderPDF
    return msDrawXXXPDF(image, shape, symbol, scalefactor ...);

and then each msDrawXXXYYY function is

int msDrawXXXYYY(image, shape, symbol, scalefactor ...) {
 do some size, width, etc adjustments with respect to minsize, maxsize, scalefactor, etc
 treat or dispatch off to other functions depending on the symbol type:
  msDrawXXXvectorYYY /*further adjustments in here: vector scaling and rotation*/
  msDrawXXXtruetypeYYY /*further adjustments in here: font lookup*/
  msDrawXXXellipseYYY /*further adjustments in here: scaling and rotation*/

Proposed way

all (or as much as possible) cartographic treatment is done once, and the renderers only deal with... tada! rendering

int msDrawXXX(image, shape, symbol, scalefactor ...) {
 do some size, width, etc adjustments with respect to minsize, maxsize, scalefactor, etc
then depending on the symbol type, switch off to a render specific function (possibly pointed at by a vtable structure):
  if vector symbol
    return msDrawXXXVector(image, shape, vector points, color, angle ...);
  else if truetype symbol
    return msDrawXXXTruetype(image, shape, font file, character, color, angle ...);

Proposed VTable Approach

I propose we attach a structure containing pointers to rendering function to the outputFormatObj

struct renderObj{
    void (*renderSimpleLine)(imageObj *img, shapeObj *p, colorObj *c, double width, int patternlength, int* pattern);
    void (*renderVectorSymbol)(imageObj*image, pointObj *location, pointObj **vectorpoints, int nvectorpoints, int/double width,
                         colorObj *color, colorObj *outlinecolor, colorObj* backgroundcolor);
    ... all the other low level rendering functions

    /* image i/o */
    imageObj* (*createImage)(int width, int height, char *imagepath, char* imageurl /*+ format specific args, eg, rgb/rgba, TODO */);
    int (*saveImage)(imageObj *img /*etc*/);

    /* helper functions */
    void (*getLabelSize)(char* font, int size, char *text /*...*/);

which would be populated when creating the outputFormatObj by

/*forward declaration of render functions defined in maprenderer.h*/
#ifdef __cplusplus
extern "C" {
MS_DLL_EXPORT void msDrawSimpleLineGD(imageObj *img, shapeObj *p, colorObj *c, double width, int patternlength, int* pattern);
MS_DLL_EXPORT void msDrawSimpleLineAGG(imageObj *img, shapeObj *p, colorObj *c, double width, int patternlength, int* pattern);
#ifdef __cplusplus

renderObj* msCreateRenderer(int type) {
    renderObj *r = malloc(sizeof(renderObj));
    switch(type) {
        return r;
        return r;
        return NULL;

the higher level functions in mapdraw.c then become:

void msDrawLineSymbol(symbolSetObj *symbolset, imageObj *image, shapeObj *p, styleObj *style, double scalefactor)
    if (image)
        if( image->format->rendererPtr!=NULL) {
            renderObj *r=image->format->rendererPtr;
            /* do some common processing of colors, widths, scalefactors, etc
            if(style->symbol == 0 || symbol->type==MS_SYMBOL_CIRCLE || symbol->type==MS_SYMBOL_SIMPLE) { /*simple line case*/
                  r->renderSimpleLine(image,p,color,nwidth,symbol->patternlength, symbol->pattern);  
            /*continue on with the other line styling possibilities
       /* continue using the old style rendering functions for the renderers
          that haven't been migrated yet to the plugin format, or for those
          where such an approach isn't feasible
       else if( MS_RENDERER_IMAGEMAP(image->format) )
            msDrawLineSymbolIM(symbolset, image, p, style, scalefactor);

        else if( MS_RENDERER_SWF(image->format) )
            msDrawLineSymbolSWF(symbolset, image, p,  style, scalefactor);
#ifdef USE_PDF
        else if( MS_RENDERER_PDF(image->format) )
            msDrawLineSymbolPDF(symbolset, image, p,  style, scalefactor);
        else if( MS_RENDERER_SVG(image->format) )
            msDrawLineSymbolSVG(symbolset, image, p,  style, scalefactor);

Desired Functionality

Line Layers

  • simple: straightforward case, need to pass vertexes, width, color, and optionally caps and joins
    • dashed
    • outlined: the renderer can provide a method to draw a solid line with a one pixel outline in a single step. Not very usefull for lines as in that case the ouline is drawn first, and the inside is cached for later processing so as to provide nice intersections.
void renderSimpleLine(imageObj *image, shapeObj *thePoints, int/double width, colorObj *color, int* dashstyle, int dashstylelength,
                          some_struct *capsandjoins)
  • brushed: used essentially with a pixmap symbol as in
    • pixmap
    • vector,truetype : investigate if this should be supported. very probably have to be transformed to a pixmap before being used internally by the renderer.
  • markers : mapserver passes a list of points and orientations to the renderer, and all the markers are rendered in one pass. pixmap, vector, truetype, and ellipse? specific versions could be presented by the renderer:
    • vector markers: the vector has been scaled to the desired size, need to pass on line width and the vector symbol points to the renderer
    • truetype markers: the font file location/lookup has been done, need to pass on font absolute path, symbol character, size
    • pixmap markers: need to pass on the pixmap symbol, and a scaling factor for the final pixmap size
    • ellipse markers: ...
      void renderXXXMarkers(imageObj *image, int nmarkers, pointObj **labelpoints, double *angles,
                               colorObj *color, colorObj *outlinecolor
                               XXXX marker type specific data);

Point Layers

should be pretty straightforward. must probably expose a way for the renderer to cache an internal representation of the symbol if needed.

  • vector: the vector points have been scaled and rotated beforehand.
    void renderVectorSymbol(imageObj*image, pointObj *location, pointObj **vectorpoints, int nvectorpoints, int/double width,
                             colorObj *color, colorObj *outlinecolor, colorObj* backgroundcolor?)
  • pixmap: need to pass on the pixmap scaling factor, angle
    void renderPixmapSymbol(imageObj *image, pointObj *location, TBD:image representation, double scalingfactor, double angle)
  • truetype: need to pass on font file, character, size, angle
    void renderTruetypeSymbol(imageObj *image, pointObj *location, char *fontfile, char character, int size, double angle,
                             colorObj *color, colorObj *outlinecolor, colorObj* backgroundcolor?)
  • ellipse: ellipse has been scaled beforehand, pass on angle?
    void renderEllipseSymbol(imageObj *image, pointObj *location, int w, int h, double angle,
                             colorObj *color, colorObj *outlinecolor, colorObj* backgroundcolor?)

Polygon and Circle Layers

I've grouped these together as they are conceptually rather equivalent. all the following will need to be split up in circle/polygon at implementation.

  • simple: straightforward, provide vertexes and color
    void renderSimplePolygon(imageObj *image, shapeObj *polypoints, colorObj *color,
                             colorObj *outlinecolor, int/double outlinewidth) /*optional outlined polygon in one step*/
  • hatched: renders only the hatch with an optional background. polygon outline would be done with another STYLE block.
    void renderHatchedPolygon(imageObj *image, shapeObj *polypoints, int/double linewidth, int/double linespacing, 
                             colorObj *color, colorObj *backgroundcolor)
  • tiled: the specified symbol is used as a tile to fill the polygon. it could be nice to provide a way to specify symbol size and spacing between symbols. Same as for marker symbols, all possible preprocessing on the actual symbol must be done before passing it on to the renderer.
    • vector: vector symbol adjusted for size and angle beforehand.
      void renderVectorTiledPolygon(imageObj *image, shapeObj *polypoints, pointObj **vectorpoints, int nvectorpoints,
                               int/double linewidth, int tileheight, int tilewidth, /*here the vector symbol would be centered on this specified tile size*/
                               colorObj *color, colorObj *outlineColor, colorObj *backgroundcolor);
    • ellipse: ellipse symbol adjusted for size
      void renderEllipseTiledPolygon(imageObj *image, shapeObj *polypoints, int/double ellipsewidth, int/double ellipseheight, double ellipseangle,
                               int tileheight, int tilewidth, /*here the ellipse symbol would be centered on this specified tile size*/
                               colorObj *color, colorObj *outlineColor, colorObj *backgroundcolor);
    • pixmap: pass on pixmap scaling factor and angle
      void renderPixmapTiledPolygon(imageObj *image, shapeObj *polypoints, TBD: image data, double pixmapscalingfactor, double pixmapangle,
                               int tileheight, int tilewidth, /*here the pixmap symbol would be centered on this specified tile size*/
                               colorObj *backgroundcolor);
    • truetype: idem, pass on font file and character
      void renderTruetypeTiledPolygon(imageObj *image, shapeObj *polypoints, char* fontfile, int charsize, double angle,
                               int tileheight, int tilewidth, /*here the truetype symbol would be centered on this specified tile size*/
                               colorObj *color, colorObj *outlineColor, colorObj *backgroundcolor);


  • font caching: gd has a global font cache, accessed through a mutex. AGG currently attaches the font cache to its imageObj.
  • encoding: gd tries to automatically detect encoding. options can either be to pass on the encoding to the renderer, or always convert to utf8 for example.
  • simple: straighforward once caching has been solved/decided
  • follow: font rendering function is called once per character. could be optimized by passing the whole string and a list of positions/angles.
  • bounding box : used by the labelcache to determine which labels can/will be drawn. With gd this is easy as the font cache is global. For other renderers, the font cache could be tied to the fontset object.

Raster Layers

The raster handling code will be updated to support a 32 bit plain RGBA buffer (in addition to GD's internal buffer format)

The renderers that internally use a pixel buffer will present an interface to export to this format, so the raster functions can directly write into it.

struct pixelbuffer {
 unsigned char *data;  //the actual data (eventually private)
 int pixelstep; //number of bytes per pixel
 int rowstep; //number of bytes per line
 unsigned char *r,*g,*b,*a; //pointers to each component of the first pixel

The renderers that don't use a pixel buffer (namely pdf and svg), will present an interface to merge a newly created RGBA buffer in this format.

I/O and Pixel Formats

All I/O is currently done with gd (or not: geotiff,etc)

pixmap input

  • leave the GD input reading code as-is if using the GD renderer.
  • provide an image reading function (using libpng, jpeg, etc) that outputs to a 32bit rgba buffer

image output

  • keep the current gd code as is for the GD rendering
  • provide image saving functions (also supporting eventual formatoptions) that read their data from a 32bit RGBA buffer.
Last modified 8 years ago Last modified on Mar 8, 2009 11:12:54 AM