= Unifying The Rendering Interfaces = page to keep track of ideas and notes concerning "pluggable" renderers, and their interface with the main mapserver program == Context == 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 === {{{ #!c 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 ...); ....etc.... } }}} and then each msDrawXXXYYY function is {{{ #!c 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: msDrawXXXpixmapYYY msDrawXXXvectorYYY /*further adjustments in here: vector scaling and rotation*/ msDrawXXXtruetypeYYY /*further adjustments in here: font lookup*/ msDrawXXXellipseYYY /*further adjustments in here: scaling and rotation*/ msDrawXXXhatchYYY } }}} === Proposed way === all (or as much as possible) cartographic treatment is done once, and the renderers only deal with... tada! rendering {{{ #!c 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 ...); else ....etc.... } }}} ---------------------------------------------------------------------------------------------------------- == Proposed VTable Approach == I propose we attach a structure containing pointers to rendering function to the outputFormatObj {{{ #!c 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 {{{ #!c /*forward declaration of render functions defined in maprenderer.h*/ #ifdef __cplusplus extern "C" { #endif 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 } #endif renderObj* msCreateRenderer(int type) { renderObj *r = malloc(sizeof(renderObj)); switch(type) { case MS_RENDER_WITH_AGG: r->renderSimpleLine=&msDrawSimpleLineAGG; /*....*/ return r; case MS_RENDER_WITH_GD: r->renderSimpleLine=&msDrawSimpleLineGD; /*.....*/ return r; case MS_RENDER_WITH_PDF: /*...*/ default: return NULL; } } }}} the higher level functions in mapdraw.c then become: {{{ #!c 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); return; } /*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); #ifdef USE_MING_FLASH else if( MS_RENDERER_SWF(image->format) ) msDrawLineSymbolSWF(symbolset, image, p, style, scalefactor); #endif #ifdef USE_PDF else if( MS_RENDERER_PDF(image->format) ) msDrawLineSymbolPDF(symbolset, image, p, style, scalefactor); #endif 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. {{{ #!c 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 [[BR]] [[Image(http://mapserver.gis.umn.edu/docs/howto/agg-rendering-specifics/linesymbolization.png)]] * 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: ... {{{ #!c 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. {{{ #!c 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 {{{ #!c void renderPixmapSymbol(imageObj *image, pointObj *location, TBD:image representation, double scalingfactor, double angle) }}} * truetype: need to pass on font file, character, size, angle {{{ #!c 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? {{{ #!c 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 {{{ #!c 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. {{{ #!c 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. {{{ #!c 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 {{{ #!c 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 {{{ #!c 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 {{{ #!c 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); }}} === Labelling === * '''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. {{{ #!c 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.